Expose additional RabbitMQ settings

Allow SSL to be configured via standard configuration as well as the
requestedHeartbeat. Switch to RabbitConnectionFactoryBean.

Closes gh-2655, gh-2676
pull/2772/merge
Stephane Nicoll 10 years ago
parent ab55331863
commit e3a124d0f9

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,9 +16,13 @@
package org.springframework.boot.autoconfigure.amqp; package org.springframework.boot.autoconfigure.amqp;
import java.io.ByteArrayOutputStream;
import java.util.Properties;
import org.springframework.amqp.core.AmqpAdmin; import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean;
import org.springframework.amqp.rabbit.core.RabbitAdmin; import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate; import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
@ -31,6 +35,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.core.io.ByteArrayResource;
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Channel;
@ -72,6 +77,7 @@ import com.rabbitmq.client.Channel;
* </ul> * </ul>
* @author Greg Turnquist * @author Greg Turnquist
* @author Josh Long * @author Josh Long
* @author Stephane Nicoll
*/ */
@Configuration @Configuration
@ConditionalOnClass({ RabbitTemplate.class, Channel.class }) @ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@ -100,10 +106,8 @@ public class RabbitAutoConfiguration {
protected static class RabbitConnectionFactoryCreator { protected static class RabbitConnectionFactoryCreator {
@Bean @Bean
public ConnectionFactory rabbitConnectionFactory(RabbitProperties config) { public ConnectionFactory rabbitConnectionFactory(RabbitProperties config) throws Exception {
CachingConnectionFactory factory = new CachingConnectionFactory(); RabbitConnectionFactoryBean factory = new RabbitConnectionFactoryBean();
String addresses = config.getAddresses();
factory.setAddresses(addresses);
if (config.getHost() != null) { if (config.getHost() != null) {
factory.setHost(config.getHost()); factory.setHost(config.getHost());
factory.setPort(config.getPort()); factory.setPort(config.getPort());
@ -117,7 +121,24 @@ public class RabbitAutoConfiguration {
if (config.getVirtualHost() != null) { if (config.getVirtualHost() != null) {
factory.setVirtualHost(config.getVirtualHost()); factory.setVirtualHost(config.getVirtualHost());
} }
return factory; if (config.getRequestedHeartbeat() != null) {
factory.setRequestedHeartbeat(config.getRequestedHeartbeat());
}
RabbitProperties.Ssl ssl = config.getSsl();
if (ssl.isEnabled()) {
factory.setUseSSL(true);
if (ssl.getKeyStore() != null || ssl.getTrustStore() != null) {
Properties properties = ssl.createSslProperties();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
properties.store(outputStream, "SSL config");
factory.setSslPropertiesLocation(
new ByteArrayResource(outputStream.toByteArray()));
}
}
factory.afterPropertiesSet();
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(factory.getObject());
connectionFactory.setAddresses(config.getAddresses());
return connectionFactory;
} }
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package org.springframework.boot.autoconfigure.amqp; package org.springframework.boot.autoconfigure.amqp;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Properties;
import java.util.Set; import java.util.Set;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
@ -27,6 +28,7 @@ import org.springframework.util.StringUtils;
* *
* @author Greg Turnquist * @author Greg Turnquist
* @author Dave Syer * @author Dave Syer
* @author Stephane Nicoll
*/ */
@ConfigurationProperties(prefix = "spring.rabbitmq") @ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitProperties { public class RabbitProperties {
@ -51,6 +53,11 @@ public class RabbitProperties {
*/ */
private String password; private String password;
/**
* SSL configuration.
*/
private final Ssl ssl = new Ssl();
/** /**
* Virtual host to use when connecting to the broker. * Virtual host to use when connecting to the broker.
*/ */
@ -61,6 +68,12 @@ public class RabbitProperties {
*/ */
private String addresses; private String addresses;
/**
* Requested heartbeat timeout, in seconds; zero for none.
*/
private Integer requestedHeartbeat;
public String getHost() { public String getHost() {
if (this.addresses == null) { if (this.addresses == null) {
return this.host; return this.host;
@ -147,6 +160,10 @@ public class RabbitProperties {
this.password = password; this.password = password;
} }
public Ssl getSsl() {
return ssl;
}
public String getVirtualHost() { public String getVirtualHost() {
return this.virtualHost; return this.virtualHost;
} }
@ -155,4 +172,102 @@ public class RabbitProperties {
this.virtualHost = ("".equals(virtualHost) ? "/" : virtualHost); this.virtualHost = ("".equals(virtualHost) ? "/" : virtualHost);
} }
public Integer getRequestedHeartbeat() {
return requestedHeartbeat;
}
public void setRequestedHeartbeat(Integer requestedHeartbeat) {
this.requestedHeartbeat = requestedHeartbeat;
}
public static class Ssl {
/**
* Enable SSL support.
*/
private boolean enabled;
/**
* Path to the key store that holds the SSL certificate.
*/
private String keyStore;
/**
* Password used to access the key store.
*/
private String keyStorePassword;
/**
* Trust store that holds SSL certificates.
*/
private String trustStore;
/**
* Password used to access the trust store.
*/
private String trustStorePassword;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getKeyStore() {
return keyStore;
}
public void setKeyStore(String keyStore) {
this.keyStore = keyStore;
}
public String getKeyStorePassword() {
return keyStorePassword;
}
public void setKeyStorePassword(String keyStorePassword) {
this.keyStorePassword = keyStorePassword;
}
public String getTrustStore() {
return trustStore;
}
public void setTrustStore(String trustStore) {
this.trustStore = trustStore;
}
public String getTrustStorePassword() {
return trustStorePassword;
}
public void setTrustStorePassword(String trustStorePassword) {
this.trustStorePassword = trustStorePassword;
}
/**
* Create the ssl configuration as expected by the
* {@link org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean RabbitConnectionFactoryBean}.
* @return the ssl configuration
*/
public Properties createSslProperties() {
Properties properties = new Properties();
if (getKeyStore() != null) {
properties.put("keyStore", getKeyStore());
}
if (getKeyStorePassword() != null) {
properties.put("keyStore.passPhrase", getKeyStorePassword());
}
if (getTrustStore() != null) {
properties.put("trustStore", getTrustStore());
}
if (getTrustStorePassword() != null) {
properties.put("trustStore.passPhrase", getTrustStorePassword());
}
return properties;
}
}
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,9 @@
package org.springframework.boot.autoconfigure.amqp; package org.springframework.boot.autoconfigure.amqp;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import org.junit.After; import org.junit.After;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@ -31,6 +34,7 @@ import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory; import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@ -47,6 +51,7 @@ import static org.mockito.Mockito.verify;
* Tests for {@link RabbitAutoConfiguration}. * Tests for {@link RabbitAutoConfiguration}.
* *
* @author Greg Turnquist * @author Greg Turnquist
* @author Stephane Nicoll
*/ */
public class RabbitAutoConfigurationTests { public class RabbitAutoConfigurationTests {
@ -188,6 +193,46 @@ public class RabbitAutoConfigurationTests {
ctx.getBean(RabbitListenerConfigUtils.RABBIT_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME); ctx.getBean(RabbitListenerConfigUtils.RABBIT_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME);
} }
@Test
public void customizeRequestedHeartBeat() {
load(TestConfiguration.class, "spring.rabbitmq.requestedHeartbeat:20");
com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory = getTargetConnectionFactory();
assertEquals(20, rabbitConnectionFactory.getRequestedHeartbeat());
}
@Test
public void noSslByDefault() {
load(TestConfiguration.class);
com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory = getTargetConnectionFactory();
assertEquals("Must use default SocketFactory", SocketFactory.getDefault(),
rabbitConnectionFactory.getSocketFactory());
}
@Test
public void enableSsl() {
load(TestConfiguration.class, "spring.rabbitmq.ssl.enabled:true");
com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory = getTargetConnectionFactory();
assertTrue("SocketFactory must use SSL", rabbitConnectionFactory.getSocketFactory() instanceof SSLSocketFactory);
}
@Test // Make sure that we at least attempt to load the store
public void enableSslWithExtraConfig() {
thrown.expectMessage("foo");
thrown.expectMessage("does not exist");
load(TestConfiguration.class, "spring.rabbitmq.ssl.enabled:true",
"spring.rabbitmq.ssl.keyStore=foo",
"spring.rabbitmq.ssl.keyStorePassword=secret",
"spring.rabbitmq.ssl.trustStore=bar",
"spring.rabbitmq.ssl.trustStorePassword=secret");
}
private com.rabbitmq.client.ConnectionFactory getTargetConnectionFactory() {
CachingConnectionFactory connectionFactory = this.context
.getBean(CachingConnectionFactory.class);
return (com.rabbitmq.client.ConnectionFactory)
new DirectFieldAccessor(connectionFactory).getPropertyValue("rabbitConnectionFactory");
}
private void load(Class<?> config, String... environment) { private void load(Class<?> config, String... environment) {
this.context = doLoad(new Class<?>[] { config }, environment); this.context = doLoad(new Class<?>[] { config }, environment);
} }

@ -428,13 +428,19 @@ content into your application; rather pick only the properties that you need.
spring.jmx.enabled=true # Expose MBeans from Spring spring.jmx.enabled=true # Expose MBeans from Spring
# RABBIT ({sc-spring-boot-autoconfigure}/amqp/RabbitProperties.{sc-ext}[RabbitProperties]) # RABBIT ({sc-spring-boot-autoconfigure}/amqp/RabbitProperties.{sc-ext}[RabbitProperties])
spring.rabbitmq.addresses= # connection addresses (e.g. myhost:9999,otherhost:1111)
spring.rabbitmq.dynamic=true # create an AmqpAdmin bean
spring.rabbitmq.host= # connection host spring.rabbitmq.host= # connection host
spring.rabbitmq.port= # connection port spring.rabbitmq.port= # connection port
spring.rabbitmq.addresses= # connection addresses (e.g. myhost:9999,otherhost:1111)
spring.rabbitmq.username= # login user
spring.rabbitmq.password= # login password spring.rabbitmq.password= # login password
spring.rabbitmq.virtual-host= spring.rabbitmq.requested-heartbeat= # requested heartbeat timeout, in seconds; zero for none
spring.rabbitmq.dynamic= spring.rabbitmq.ssl.enabled=false # enable SSL support
spring.rabbitmq.ssl.key-store= # path to the key store that holds the SSL certificate
spring.rabbitmq.ssl.key-store-password= # password used to access the key store
spring.rabbitmq.ssl.trust-store= # trust store that holds SSL certificates
spring.rabbitmq.ssl.trust-store-password= # password used to access the trust store
spring.rabbitmq.username= # login user
spring.rabbitmq.virtual-host= # virtual host to use when connecting to the broker
# REDIS ({sc-spring-boot-autoconfigure}/redis/RedisProperties.{sc-ext}[RedisProperties]) # REDIS ({sc-spring-boot-autoconfigure}/redis/RedisProperties.{sc-ext}[RedisProperties])
spring.redis.database= # database name spring.redis.database= # database name

Loading…
Cancel
Save