diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java index 027fff1132d..3959eb33b8c 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java @@ -90,18 +90,19 @@ public class RabbitAutoConfiguration { public CachingConnectionFactory rabbitConnectionFactory(RabbitProperties config) throws Exception { RabbitConnectionFactoryBean factory = new RabbitConnectionFactoryBean(); - if (config.getHost() != null) { - factory.setHost(config.getHost()); - factory.setPort(config.getPort()); + if (config.determineHost() != null) { + factory.setHost(config.determineHost()); } - if (config.getUsername() != null) { - factory.setUsername(config.getUsername()); + factory.setPort(config.determinePort()); + factory.setHost(config.determineHost()); + if (config.determineUsername() != null) { + factory.setUsername(config.determineUsername()); } - if (config.getPassword() != null) { - factory.setPassword(config.getPassword()); + if (config.determinePassword() != null) { + factory.setPassword(config.determinePassword()); } - if (config.getVirtualHost() != null) { - factory.setVirtualHost(config.getVirtualHost()); + if (config.determineVirtualHost() != null) { + factory.setVirtualHost(config.determineVirtualHost()); } if (config.getRequestedHeartbeat() != null) { factory.setRequestedHeartbeat(config.getRequestedHeartbeat()); @@ -123,7 +124,7 @@ public class RabbitAutoConfiguration { factory.afterPropertiesSet(); CachingConnectionFactory connectionFactory = new CachingConnectionFactory( factory.getObject()); - connectionFactory.setAddresses(config.getAddresses()); + connectionFactory.setAddresses(config.determineAddresses()); connectionFactory.setPublisherConfirms(config.isPublisherConfirms()); connectionFactory.setPublisherReturns(config.isPublisherReturns()); if (config.getCache().getChannel().getSize() != null) { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitProperties.java index ad197c9e991..850f6656d07 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitProperties.java @@ -16,13 +16,14 @@ package org.springframework.boot.autoconfigure.amqp; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; import org.springframework.amqp.core.AcknowledgeMode; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory.CacheMode; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.NestedConfigurationProperty; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; /** @@ -105,15 +106,24 @@ public class RabbitProperties { private final Template template = new Template(); + private List
parsedAddresses; + public String getHost() { - if (this.addresses == null) { - return this.host; + return this.host; + } + + /** + * Returns the host from the first address, or the configured host if no addresses + * have been set. + * @return the host + * @see #setAddresses(String) + * @see #getHost() + */ + public String determineHost() { + if (CollectionUtils.isEmpty(this.parsedAddresses)) { + return getHost(); } - String[] hosts = StringUtils.delimitedListToStringArray(this.addresses, ":"); - if (hosts.length == 2) { - return hosts[0]; - } - return null; + return this.parsedAddresses.get(0).host; } public void setHost(String host) { @@ -121,64 +131,80 @@ public class RabbitProperties { } public int getPort() { - if (this.addresses == null) { - return this.port; - } - String[] hosts = StringUtils.delimitedListToStringArray(this.addresses, ":"); - if (hosts.length >= 2) { - return Integer - .valueOf(StringUtils.commaDelimitedListToStringArray(hosts[1])[0]); - } return this.port; } - public void setAddresses(String addresses) { - this.addresses = parseAddresses(addresses); - } - - public String getAddresses() { - return (this.addresses == null ? this.host + ":" + this.port : this.addresses); - } - - private String parseAddresses(String addresses) { - Set result = new LinkedHashSet(); - for (String address : StringUtils.commaDelimitedListToStringArray(addresses)) { - address = address.trim(); - if (address.startsWith("amqp://")) { - address = address.substring("amqp://".length()); - } - if (address.contains("@")) { - String[] split = StringUtils.split(address, "@"); - String creds = split[0]; - address = split[1]; - split = StringUtils.split(creds, ":"); - this.username = split[0]; - if (split.length > 0) { - this.password = split[1]; - } - } - int index = address.indexOf("/"); - if (index >= 0 && index < address.length()) { - setVirtualHost(address.substring(index + 1)); - address = address.substring(0, index); - } - if (!address.contains(":")) { - address = address + ":" + this.port; - } - result.add(address); + /** + * Returns the port from the first address, or the configured port if no addresses + * have been set. + * @return the port + * @see #setAddresses(String) + * @see #getPort() + */ + public int determinePort() { + if (CollectionUtils.isEmpty(this.parsedAddresses)) { + return getPort(); } - return (result.isEmpty() ? null - : StringUtils.collectionToCommaDelimitedString(result)); + Address address = this.parsedAddresses.get(0); + return address.port; } public void setPort(int port) { this.port = port; } + public String getAddresses() { + return this.addresses; + } + + /** + * Returns the comma-separated addresses or a single address ({@code host:port}) + * created from the configured host and port if no addresses have been set. + * @return the addresses + */ + public String determineAddresses() { + if (CollectionUtils.isEmpty(this.parsedAddresses)) { + return this.host + ":" + this.port; + } + List addressStrings = new ArrayList(); + for (Address parsedAddress : this.parsedAddresses) { + addressStrings.add(parsedAddress.host + ":" + parsedAddress.port); + } + return StringUtils.collectionToCommaDelimitedString(addressStrings); + } + + public void setAddresses(String addresses) { + this.addresses = addresses; + this.parsedAddresses = parseAddresses(addresses); + } + + private List
parseAddresses(String addresses) { + List
parsedAddresses = new ArrayList
(); + for (String address : StringUtils.commaDelimitedListToStringArray(addresses)) { + parsedAddresses.add(new Address(address)); + } + return parsedAddresses; + } + public String getUsername() { return this.username; } + /** + * If addresses have been set and the first address has a username it is returned. + * Otherwise returns the result of calling {@code getUsername()}. + * @return the username + * @see #setAddresses(String) + * @see #getUsername() + */ + public String determineUsername() { + if (CollectionUtils.isEmpty(this.parsedAddresses)) { + return this.username; + } + Address address = this.parsedAddresses.get(0); + return address.username == null ? this.username : address.username; + } + public void setUsername(String username) { this.username = username; } @@ -187,6 +213,21 @@ public class RabbitProperties { return this.password; } + /** + * If addresses have been set and the first address has a password it is returned. + * Otherwise returns the result of calling {@code getPassword()}. + * @return the password or {@code null} + * @see #setAddresses(String) + * @see #getPassword() + */ + public String determinePassword() { + if (CollectionUtils.isEmpty(this.parsedAddresses)) { + return getPassword(); + } + Address address = this.parsedAddresses.get(0); + return address.password == null ? getPassword() : address.password; + } + public void setPassword(String password) { this.password = password; } @@ -199,6 +240,21 @@ public class RabbitProperties { return this.virtualHost; } + /** + * If addresses have been set and the first address has a virtual host it is returned. + * Otherwise returns the result of calling {@code getVirtualHost()}. + * @return the password or {@code null} + * @see #setAddresses(String) + * @see #getVirtualHost() + */ + public String determineVirtualHost() { + if (CollectionUtils.isEmpty(this.parsedAddresses)) { + return getVirtualHost(); + } + Address address = this.parsedAddresses.get(0); + return address.virtualHost == null ? getVirtualHost() : address.virtualHost; + } + public void setVirtualHost(String virtualHost) { this.virtualHost = ("".equals(virtualHost) ? "/" : virtualHost); } @@ -652,4 +708,75 @@ public class RabbitProperties { } + private static final class Address { + + private static final String PREFIX_AMQP = "amqp://"; + + private static final int DEFAULT_PORT = 5672; + + private String host; + + private int port; + + private String username; + + private String password; + + private String virtualHost; + + private Address(String input) { + input = input.trim(); + input = trimPrefix(input); + input = parseUsernameAndPassword(input); + input = parseVirtualHost(input); + parseHostAndPort(input); + } + + private String trimPrefix(String input) { + if (input.startsWith(PREFIX_AMQP)) { + input = input.substring(PREFIX_AMQP.length()); + } + return input; + } + + private String parseUsernameAndPassword(String input) { + if (input.contains("@")) { + String[] split = StringUtils.split(input, "@"); + String creds = split[0]; + input = split[1]; + split = StringUtils.split(creds, ":"); + this.username = split[0]; + if (split.length > 0) { + this.password = split[1]; + } + } + return input; + } + + private String parseVirtualHost(String input) { + int hostIndex = input.indexOf("/"); + if (hostIndex >= 0 && hostIndex < input.length()) { + this.virtualHost = input.substring(hostIndex + 1); + if (this.virtualHost.length() == 0) { + this.virtualHost = "/"; + } + input = input.substring(0, hostIndex); + } + return input; + } + + private void parseHostAndPort(String input) { + int portIndex = input.indexOf(':'); + if (portIndex == -1) { + this.host = input; + this.port = DEFAULT_PORT; + } + else { + this.host = input.substring(0, portIndex); + this.port = Integer.valueOf(input.substring(portIndex + 1)); + } + } + + } + } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java index a777c129e4c..a0c54db8fdc 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java @@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.amqp; import javax.net.SocketFactory; import javax.net.ssl.SSLSocketFactory; +import com.rabbitmq.client.Address; import org.aopalliance.aop.Advice; import org.junit.After; import org.junit.Rule; @@ -112,6 +113,8 @@ public class RabbitAutoConfigurationTests { com.rabbitmq.client.ConnectionFactory rcf = (com.rabbitmq.client.ConnectionFactory) dfa .getPropertyValue("rabbitConnectionFactory"); assertThat(rcf.getConnectionTimeout()).isEqualTo(123); + assertThat((Address[]) dfa.getPropertyValue("addresses")).hasSize(1); + } @Test diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitPropertiesTests.java index 8c859a00e4d..0dcd5fab8ad 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitPropertiesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitPropertiesTests.java @@ -31,91 +31,199 @@ public class RabbitPropertiesTests { private final RabbitProperties properties = new RabbitProperties(); @Test - public void addressesNotSet() { + public void hostDefaultsToLocalhost() { assertThat(this.properties.getHost()).isEqualTo("localhost"); + } + + @Test + public void customHost() { + this.properties.setHost("rabbit.example.com"); + assertThat(this.properties.getHost()).isEqualTo("rabbit.example.com"); + } + + @Test + public void hostIsDeterminedFromFirstAddress() { + this.properties.setAddresses("rabbit1.example.com:1234,rabbit2.example.com:2345"); + assertThat(this.properties.determineHost()).isEqualTo("rabbit1.example.com"); + } + + @Test + public void determineHostReturnsHostPropertyWhenNoAddresses() { + this.properties.setHost("rabbit.example.com"); + assertThat(this.properties.determineHost()).isEqualTo("rabbit.example.com"); + } + + @Test + public void portDefaultsTo5672() { assertThat(this.properties.getPort()).isEqualTo(5672); } @Test - public void addressesSingleValued() { - this.properties.setAddresses("myhost:9999"); - assertThat(this.properties.getHost()).isEqualTo("myhost"); - assertThat(this.properties.getPort()).isEqualTo(9999); + public void customPort() { + this.properties.setPort(1234); + assertThat(this.properties.getPort()).isEqualTo(1234); } @Test - public void addressesDoubleValued() { - this.properties.setAddresses("myhost:9999,otherhost:1111"); - assertThat(this.properties.getHost()).isNull(); - assertThat(this.properties.getPort()).isEqualTo(9999); + public void determinePortReturnsPortOfFirstAddress() { + this.properties.setAddresses("rabbit1.example.com:1234,rabbit2.example.com:2345"); + assertThat(this.properties.determinePort()).isEqualTo(1234); } @Test - public void addressesDoubleValuedWithCredentials() { - this.properties.setAddresses("myhost:9999,root:password@otherhost:1111/host"); - assertThat(this.properties.getHost()).isNull(); - assertThat(this.properties.getPort()).isEqualTo(9999); - assertThat(this.properties.getUsername()).isEqualTo("root"); - assertThat(this.properties.getVirtualHost()).isEqualTo("host"); + public void determinePortReturnsPortPropertyWhenNoAddresses() { + this.properties.setPort(1234); + assertThat(this.properties.determinePort()).isEqualTo(1234); } @Test - public void addressesDoubleValuedPreservesOrder() { - this.properties.setAddresses("myhost:9999,ahost:1111/host"); - assertThat(this.properties.getHost()).isNull(); - assertThat(this.properties.getAddresses()).isEqualTo("myhost:9999,ahost:1111"); + public void determinePortReturnsDefaultAmqpPortWhenFirstAddressHasNoExplicitPort() { + this.properties.setPort(1234); + this.properties.setAddresses("rabbit1.example.com,rabbit2.example.com:2345"); + assertThat(this.properties.determinePort()).isEqualTo(5672); } @Test - public void addressesSingleValuedWithCredentials() { - this.properties.setAddresses("amqp://root:password@otherhost:1111/host"); - assertThat(this.properties.getHost()).isEqualTo("otherhost"); - assertThat(this.properties.getPort()).isEqualTo(1111); - assertThat(this.properties.getUsername()).isEqualTo("root"); - assertThat(this.properties.getVirtualHost()).isEqualTo("host"); + public void virtualHostDefaultsToNull() { + assertThat(this.properties.getVirtualHost()).isNull(); } @Test - public void addressesSingleValuedWithCredentialsDefaultPort() { - this.properties.setAddresses("amqp://root:password@lemur.cloudamqp.com/host"); - assertThat(this.properties.getHost()).isEqualTo("lemur.cloudamqp.com"); - assertThat(this.properties.getPort()).isEqualTo(5672); - assertThat(this.properties.getUsername()).isEqualTo("root"); - assertThat(this.properties.getVirtualHost()).isEqualTo("host"); - assertThat(this.properties.getAddresses()).isEqualTo("lemur.cloudamqp.com:5672"); + public void customVirtualHost() { + this.properties.setVirtualHost("alpha"); + assertThat(this.properties.getVirtualHost()).isEqualTo("alpha"); } @Test - public void addressWithTrailingSlash() { + public void virtualHostRetainsALeadingSlash() { + this.properties.setVirtualHost("/alpha"); + assertThat(this.properties.getVirtualHost()).isEqualTo("/alpha"); + } + + @Test + public void determineVirtualHostReturnsVirtualHostOfFirstAddress() { + this.properties.setAddresses( + "rabbit1.example.com:1234/alpha,rabbit2.example.com:2345/bravo"); + assertThat(this.properties.determineVirtualHost()).isEqualTo("alpha"); + } + + @Test + public void determineVirtualHostReturnsPropertyWhenNoAddresses() { + this.properties.setVirtualHost("alpha"); + assertThat(this.properties.determineVirtualHost()).isEqualTo("alpha"); + } + + @Test + public void determineVirtualHostReturnsPropertyWhenFirstAddressHasNoVirtualHost() { + this.properties.setVirtualHost("alpha"); + this.properties + .setAddresses("rabbit1.example.com:1234,rabbit2.example.com:2345/bravo"); + assertThat(this.properties.determineVirtualHost()).isEqualTo("alpha"); + } + + @Test + public void determinedVirtualHostIsSlashWhenAddressHasTrailingSlash() { this.properties.setAddresses("amqp://root:password@otherhost:1111/"); - assertThat(this.properties.getHost()).isEqualTo("otherhost"); - assertThat(this.properties.getPort()).isEqualTo(1111); - assertThat(this.properties.getUsername()).isEqualTo("root"); - assertThat(this.properties.getVirtualHost()).isEqualTo("/"); + assertThat(this.properties.determineVirtualHost()).isEqualTo("/"); } @Test - public void testDefaultVirtualHost() { - this.properties.setVirtualHost("/"); - assertThat(this.properties.getVirtualHost()).isEqualTo("/"); - } - - @Test - public void testEmptyVirtualHost() { + public void emptyVirtualHostIsCoercedToASlash() { this.properties.setVirtualHost(""); assertThat(this.properties.getVirtualHost()).isEqualTo("/"); } @Test - public void testCustomVirtualHost() { - this.properties.setVirtualHost("myvHost"); - assertThat(this.properties.getVirtualHost()).isEqualTo("myvHost"); + public void usernameDefaultsToNull() { + assertThat(this.properties.getUsername()).isNull(); } @Test - public void testCustomFalsyVirtualHost() { - this.properties.setVirtualHost("/myvHost"); - assertThat(this.properties.getVirtualHost()).isEqualTo("/myvHost"); + public void customUsername() { + this.properties.setUsername("user"); + assertThat(this.properties.getUsername()).isEqualTo("user"); + } + + @Test + public void determineUsernameReturnsUsernameOfFirstAddress() { + this.properties.setAddresses("user:secret@rabbit1.example.com:1234/alpha," + + "rabbit2.example.com:2345/bravo"); + assertThat(this.properties.determineUsername()).isEqualTo("user"); + } + + @Test + public void determineUsernameReturnsPropertyWhenNoAddresses() { + this.properties.setUsername("alice"); + assertThat(this.properties.determineUsername()).isEqualTo("alice"); + } + + @Test + public void determineUsernameReturnsPropertyWhenFirstAddressHasNoUsername() { + this.properties.setUsername("alice"); + this.properties.setAddresses("rabbit1.example.com:1234/alpha," + + "user:secret@rabbit2.example.com:2345/bravo"); + assertThat(this.properties.determineUsername()).isEqualTo("alice"); + } + + @Test + public void passwordDefaultsToNull() { + assertThat(this.properties.getPassword()).isNull(); + } + + @Test + public void customPassword() { + this.properties.setPassword("secret"); + assertThat(this.properties.getPassword()).isEqualTo("secret"); + } + + @Test + public void determinePasswordReturnsPasswordOfFirstAddress() { + this.properties.setAddresses("user:secret@rabbit1.example.com:1234/alpha," + + "rabbit2.example.com:2345/bravo"); + assertThat(this.properties.determinePassword()).isEqualTo("secret"); + } + + @Test + public void determinePasswordReturnsPropertyWhenNoAddresses() { + this.properties.setPassword("secret"); + assertThat(this.properties.determinePassword()).isEqualTo("secret"); + } + + @Test + public void determinePasswordReturnsPropertyWhenFirstAddressHasNoPassword() { + this.properties.setPassword("12345678"); + this.properties.setAddresses("rabbit1.example.com:1234/alpha," + + "user:secret@rabbit2.example.com:2345/bravo"); + assertThat(this.properties.determinePassword()).isEqualTo("12345678"); + } + + @Test + public void addressesDefaultsToNull() { + assertThat(this.properties.getAddresses()).isEqualTo(null); + } + + @Test + public void customAddresses() { + this.properties.setAddresses( + "user:secrect@rabbit1.example.com:1234/alpha,rabbit2.example.com"); + assertThat(this.properties.getAddresses()).isEqualTo( + "user:secrect@rabbit1.example.com:1234/alpha,rabbit2.example.com"); + } + + @Test + public void determineAddressesReturnsAddressesWithJustHostAndPort() { + this.properties.setAddresses( + "user:secrect@rabbit1.example.com:1234/alpha,rabbit2.example.com"); + assertThat(this.properties.determineAddresses()) + .isEqualTo("rabbit1.example.com:1234,rabbit2.example.com:5672"); + } + + @Test + public void determineAddressesUsesHostAndPortPropertiesWhenNoAddressesSet() { + this.properties.setHost("rabbit.example.com"); + this.properties.setPort(1234); + assertThat(this.properties.determineAddresses()) + .isEqualTo("rabbit.example.com:1234"); } }