Commit e3a124d0 authored by Stephane Nicoll's avatar Stephane Nicoll

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
parent ab553318
/* /*
* 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 @@ ...@@ -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 ...@@ -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; ...@@ -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 { ...@@ -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 { ...@@ -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;
} }
} }
......
/* /*
* 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 @@ ...@@ -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; ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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;
}
}
} }
/* /*
* 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 @@ ...@@ -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; ...@@ -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; ...@@ -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 { ...@@ -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. ...@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment