Skip to content

Commit 69f31cb

Browse files
wilkinsonamhalbritterphilwebb
committed
Add ConnectionDetail support to Rabbit auto-configuration
Update Rabbit auto-configuration so that `RabbitConnectionDetails` beans may be optionally used to provide connection details. See gh-34657 Co-Authored-By: Mortitz Halbritter <[email protected]> Co-Authored-By: Phillip Webb <[email protected]>
1 parent de8fb04 commit 69f31cb

File tree

7 files changed

+312
-28
lines changed

7 files changed

+312
-28
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/AbstractConnectionFactoryConfigurer.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.boot.autoconfigure.amqp;
1818

19+
import java.util.stream.Collectors;
20+
1921
import org.springframework.amqp.rabbit.connection.AbstractConnectionFactory;
2022
import org.springframework.amqp.rabbit.connection.ConnectionNameStrategy;
2123
import org.springframework.boot.context.properties.PropertyMapper;
@@ -27,6 +29,9 @@
2729
*
2830
* @param <T> the connection factory type.
2931
* @author Chris Bono
32+
* @author Moritz Halbritter
33+
* @author Andy Wilkinson
34+
* @author Phillip Webb
3035
* @since 2.6.0
3136
*/
3237
public abstract class AbstractConnectionFactoryConfigurer<T extends AbstractConnectionFactory> {
@@ -35,9 +40,31 @@ public abstract class AbstractConnectionFactoryConfigurer<T extends AbstractConn
3540

3641
private ConnectionNameStrategy connectionNameStrategy;
3742

43+
private final RabbitConnectionDetails connectionDetails;
44+
45+
/**
46+
* Creates a new configurer that will configure the connection factory using the given
47+
* {@code properties}.
48+
* @param properties the properties to use to configure the connection factory
49+
*/
3850
protected AbstractConnectionFactoryConfigurer(RabbitProperties properties) {
39-
Assert.notNull(properties, "RabbitProperties must not be null");
51+
this(properties, new PropertiesRabbitConnectionDetails(properties));
52+
}
53+
54+
/**
55+
* Creates a new configurer that will configure the connection factory using the given
56+
* {@code properties} and {@code connectionDetails}, with the latter taking priority.
57+
* @param properties the properties to use to configure the connection factory
58+
* @param connectionDetails the connection details to use to configure the connection
59+
* factory
60+
* @since 3.1.0
61+
*/
62+
protected AbstractConnectionFactoryConfigurer(RabbitProperties properties,
63+
RabbitConnectionDetails connectionDetails) {
64+
Assert.notNull(properties, "Properties must not be null");
65+
Assert.notNull(connectionDetails, "ConnectionDetails must not be null");
4066
this.rabbitProperties = properties;
67+
this.connectionDetails = connectionDetails;
4168
}
4269

4370
protected final ConnectionNameStrategy getConnectionNameStrategy() {
@@ -55,7 +82,11 @@ public final void setConnectionNameStrategy(ConnectionNameStrategy connectionNam
5582
public final void configure(T connectionFactory) {
5683
Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
5784
PropertyMapper map = PropertyMapper.get();
58-
map.from(this.rabbitProperties::determineAddresses).to(connectionFactory::setAddresses);
85+
String addresses = this.connectionDetails.getAddresses()
86+
.stream()
87+
.map((address) -> address.host() + ":" + address.port())
88+
.collect(Collectors.joining(","));
89+
map.from(addresses).to(connectionFactory::setAddresses);
5990
map.from(this.rabbitProperties::getAddressShuffleMode)
6091
.whenNonNull()
6192
.to(connectionFactory::setAddressShuffleMode);

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/CachingConnectionFactoryConfigurer.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,32 @@
2525
* Configures Rabbit {@link CachingConnectionFactory} with sensible defaults.
2626
*
2727
* @author Chris Bono
28+
* @author Moritz Halbritter
29+
* @author Andy Wilkinson
30+
* @author Phillip Webb
2831
* @since 2.6.0
2932
*/
3033
public class CachingConnectionFactoryConfigurer extends AbstractConnectionFactoryConfigurer<CachingConnectionFactory> {
3134

35+
/**
36+
* Creates a new configurer that will configure the connection factory using the given
37+
* {@code properties}.
38+
* @param properties the properties to use to configure the connection factory
39+
*/
3240
public CachingConnectionFactoryConfigurer(RabbitProperties properties) {
33-
super(properties);
41+
this(properties, new PropertiesRabbitConnectionDetails(properties));
42+
}
43+
44+
/**
45+
* Creates a new configurer that will configure the connection factory using the given
46+
* {@code properties} and {@code connectionDetails}, with the latter taking priority.
47+
* @param properties the properties to use to configure the connection factory
48+
* @param connectionDetails the connection details to use to configure the connection
49+
* factory
50+
* @since 3.1.0
51+
*/
52+
public CachingConnectionFactoryConfigurer(RabbitProperties properties, RabbitConnectionDetails connectionDetails) {
53+
super(properties, connectionDetails);
3454
}
3555

3656
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.amqp;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
/**
23+
* Adapts {@link RabbitProperties} to {@link RabbitConnectionDetails}.
24+
*
25+
* @author Moritz Halbritter
26+
* @author Andy Wilkinson
27+
* @author Phillip Webb
28+
* @since 3.1.0
29+
*/
30+
public class PropertiesRabbitConnectionDetails implements RabbitConnectionDetails {
31+
32+
private final RabbitProperties properties;
33+
34+
public PropertiesRabbitConnectionDetails(RabbitProperties properties) {
35+
this.properties = properties;
36+
}
37+
38+
@Override
39+
public String getUsername() {
40+
return this.properties.determineUsername();
41+
}
42+
43+
@Override
44+
public String getPassword() {
45+
return this.properties.determinePassword();
46+
}
47+
48+
@Override
49+
public String getVirtualHost() {
50+
return this.properties.determineVirtualHost();
51+
}
52+
53+
@Override
54+
public List<Address> getAddresses() {
55+
List<Address> addresses = new ArrayList<>();
56+
for (String address : this.properties.determineAddresses().split(",")) {
57+
String[] components = address.split(":");
58+
addresses.add(new Address(components[0], Integer.parseInt(components[1])));
59+
}
60+
return addresses;
61+
}
62+
63+
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,6 @@
5959
* <li>{@link org.springframework.amqp.core.AmqpAdmin } instance as long as
6060
* {@literal spring.rabbitmq.dynamic=true}.</li>
6161
* </ul>
62-
* <p>
63-
* The {@link org.springframework.amqp.rabbit.connection.CachingConnectionFactory} honors
64-
* the following properties:
65-
* <ul>
66-
* <li>{@literal spring.rabbitmq.port} is used to specify the port to which the client
67-
* should connect, and defaults to 5672.</li>
68-
* <li>{@literal spring.rabbitmq.username} is used to specify the (optional) username.
69-
* </li>
70-
* <li>{@literal spring.rabbitmq.password} is used to specify the (optional) password.
71-
* </li>
72-
* <li>{@literal spring.rabbitmq.host} is used to specify the host, and defaults to
73-
* {@literal localhost}.</li>
74-
* <li>{@literal spring.rabbitmq.virtualHost} is used to specify the (optional) virtual
75-
* host to which the client should connect.</li>
76-
* </ul>
7762
*
7863
* @author Greg Turnquist
7964
* @author Josh Long
@@ -82,6 +67,8 @@
8267
* @author Phillip Webb
8368
* @author Artsiom Yudovin
8469
* @author Chris Bono
70+
* @author Moritz Halbritter
71+
* @author Andy Wilkinson
8572
* @since 1.0.0
8673
*/
8774
@AutoConfiguration
@@ -93,23 +80,35 @@ public class RabbitAutoConfiguration {
9380
@Configuration(proxyBeanMethods = false)
9481
protected static class RabbitConnectionFactoryCreator {
9582

83+
private final RabbitProperties properties;
84+
85+
private final RabbitConnectionDetails connectionDetails;
86+
87+
protected RabbitConnectionFactoryCreator(RabbitProperties properties,
88+
ObjectProvider<RabbitConnectionDetails> connectionDetails) {
89+
this.properties = properties;
90+
this.connectionDetails = connectionDetails
91+
.getIfAvailable(() -> new PropertiesRabbitConnectionDetails(properties));
92+
}
93+
9694
@Bean
9795
@ConditionalOnMissingBean
98-
RabbitConnectionFactoryBeanConfigurer rabbitConnectionFactoryBeanConfigurer(RabbitProperties properties,
99-
ResourceLoader resourceLoader, ObjectProvider<CredentialsProvider> credentialsProvider,
96+
RabbitConnectionFactoryBeanConfigurer rabbitConnectionFactoryBeanConfigurer(ResourceLoader resourceLoader,
97+
ObjectProvider<CredentialsProvider> credentialsProvider,
10098
ObjectProvider<CredentialsRefreshService> credentialsRefreshService) {
10199
RabbitConnectionFactoryBeanConfigurer configurer = new RabbitConnectionFactoryBeanConfigurer(resourceLoader,
102-
properties);
100+
this.properties, this.connectionDetails);
103101
configurer.setCredentialsProvider(credentialsProvider.getIfUnique());
104102
configurer.setCredentialsRefreshService(credentialsRefreshService.getIfUnique());
105103
return configurer;
106104
}
107105

108106
@Bean
109107
@ConditionalOnMissingBean
110-
CachingConnectionFactoryConfigurer rabbitConnectionFactoryConfigurer(RabbitProperties rabbitProperties,
108+
CachingConnectionFactoryConfigurer rabbitConnectionFactoryConfigurer(
111109
ObjectProvider<ConnectionNameStrategy> connectionNameStrategy) {
112-
CachingConnectionFactoryConfigurer configurer = new CachingConnectionFactoryConfigurer(rabbitProperties);
110+
CachingConnectionFactoryConfigurer configurer = new CachingConnectionFactoryConfigurer(this.properties,
111+
this.connectionDetails);
113112
configurer.setConnectionNameStrategy(connectionNameStrategy.getIfUnique());
114113
return configurer;
115114
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.amqp;
18+
19+
import java.util.List;
20+
21+
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
22+
import org.springframework.util.Assert;
23+
24+
/**
25+
* Details required to establish a connection to a RabbitMQ service.
26+
*
27+
* @author Moritz Halbritter
28+
* @author Andy Wilkinson
29+
* @author Phillip Webb
30+
* @since 3.1.0
31+
*/
32+
public interface RabbitConnectionDetails extends ConnectionDetails {
33+
34+
/**
35+
* Login user to authenticate to the broker.
36+
* @return the login user to authenticate to the broker or {@code null}
37+
*/
38+
default String getUsername() {
39+
return null;
40+
}
41+
42+
/**
43+
* Login to authenticate against the broker.
44+
* @return the login to authenticate against the broker or {@code null}
45+
*/
46+
default String getPassword() {
47+
return null;
48+
}
49+
50+
/**
51+
* Virtual host to use when connecting to the broker.
52+
* @return the virtual host to use when connecting to the broker or {@code null}
53+
*/
54+
default String getVirtualHost() {
55+
return null;
56+
}
57+
58+
/**
59+
* List of addresses to which the client should connect. Must return at least one
60+
* address.
61+
* @return the list of addresses to which the client should connect
62+
*/
63+
List<Address> getAddresses();
64+
65+
/**
66+
* Returns the first address.
67+
* @return the first address
68+
* @throws IllegalStateException if the address list is empty
69+
*/
70+
default Address getFirstAddress() {
71+
List<Address> addresses = getAddresses();
72+
Assert.state(!addresses.isEmpty(), "Address list is empty");
73+
return addresses.get(0);
74+
}
75+
76+
/**
77+
* A RabbitMQ address.
78+
*
79+
* @param host the host
80+
* @param port the port
81+
*/
82+
record Address(String host, int port) {
83+
}
84+
85+
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitConnectionFactoryBeanConfigurer.java

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.rabbitmq.client.impl.CredentialsRefreshService;
2323

2424
import org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean;
25+
import org.springframework.boot.autoconfigure.amqp.RabbitConnectionDetails.Address;
2526
import org.springframework.boot.context.properties.PropertyMapper;
2627
import org.springframework.core.io.ResourceLoader;
2728
import org.springframework.util.Assert;
@@ -30,6 +31,9 @@
3031
* Configures {@link RabbitConnectionFactoryBean} with sensible defaults.
3132
*
3233
* @author Chris Bono
34+
* @author Moritz Halbritter
35+
* @author Andy Wilkinson
36+
* @author Phillip Webb
3337
* @since 2.6.0
3438
*/
3539
public class RabbitConnectionFactoryBeanConfigurer {
@@ -38,13 +42,39 @@ public class RabbitConnectionFactoryBeanConfigurer {
3842

3943
private final ResourceLoader resourceLoader;
4044

45+
private final RabbitConnectionDetails connectionDetails;
46+
4147
private CredentialsProvider credentialsProvider;
4248

4349
private CredentialsRefreshService credentialsRefreshService;
4450

51+
/**
52+
* Creates a new configurer that will use the given {@code resourceLoader} and
53+
* {@code properties}.
54+
* @param resourceLoader the resource loader
55+
* @param properties the properties
56+
*/
4557
public RabbitConnectionFactoryBeanConfigurer(ResourceLoader resourceLoader, RabbitProperties properties) {
58+
this(resourceLoader, properties, new PropertiesRabbitConnectionDetails(properties));
59+
}
60+
61+
/**
62+
* Creates a new configurer that will use the given {@code resourceLoader},
63+
* {@code properties}, and {@code connectionDetails}. The connection details have
64+
* priority over the properties.
65+
* @param resourceLoader the resource loader
66+
* @param properties the properties
67+
* @param connectionDetails the connection details.
68+
* @since 3.1.0
69+
*/
70+
public RabbitConnectionFactoryBeanConfigurer(ResourceLoader resourceLoader, RabbitProperties properties,
71+
RabbitConnectionDetails connectionDetails) {
72+
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
73+
Assert.notNull(properties, "Properties must not be null");
74+
Assert.notNull(connectionDetails, "ConnectionDetails must not be null");
4675
this.resourceLoader = resourceLoader;
4776
this.rabbitProperties = properties;
77+
this.connectionDetails = connectionDetails;
4878
}
4979

5080
public void setCredentialsProvider(CredentialsProvider credentialsProvider) {
@@ -65,12 +95,13 @@ public void setCredentialsRefreshService(CredentialsRefreshService credentialsRe
6595
public void configure(RabbitConnectionFactoryBean factory) {
6696
Assert.notNull(factory, "RabbitConnectionFactoryBean must not be null");
6797
factory.setResourceLoader(this.resourceLoader);
98+
Address address = this.connectionDetails.getFirstAddress();
6899
PropertyMapper map = PropertyMapper.get();
69-
map.from(this.rabbitProperties::determineHost).whenNonNull().to(factory::setHost);
70-
map.from(this.rabbitProperties::determinePort).to(factory::setPort);
71-
map.from(this.rabbitProperties::determineUsername).whenNonNull().to(factory::setUsername);
72-
map.from(this.rabbitProperties::determinePassword).whenNonNull().to(factory::setPassword);
73-
map.from(this.rabbitProperties::determineVirtualHost).whenNonNull().to(factory::setVirtualHost);
100+
map.from(address::host).whenNonNull().to(factory::setHost);
101+
map.from(address::port).to(factory::setPort);
102+
map.from(this.connectionDetails::getUsername).whenNonNull().to(factory::setUsername);
103+
map.from(this.connectionDetails::getPassword).whenNonNull().to(factory::setPassword);
104+
map.from(this.connectionDetails::getVirtualHost).whenNonNull().to(factory::setVirtualHost);
74105
map.from(this.rabbitProperties::getRequestedHeartbeat)
75106
.whenNonNull()
76107
.asInt(Duration::getSeconds)

0 commit comments

Comments
 (0)