mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-17 12:09:16 +00:00
Add AMQP 1.0 RabbitMQ auto-configuration
See gh-49857 Signed-off-by: Eddú Meléndez <eddu.melendez@gmail.com>
This commit is contained in:
committed by
Stéphane Nicoll
parent
ebac87ae65
commit
0ed57a6109
@@ -95,6 +95,7 @@ dependencies {
|
||||
implementation(project(path: ":module:spring-boot-actuator"))
|
||||
implementation(project(path: ":module:spring-boot-actuator-autoconfigure"))
|
||||
implementation(project(path: ":module:spring-boot-amqp"))
|
||||
implementation(project(path: ":module:spring-boot-amqp-rabbitmq"))
|
||||
implementation(project(path: ":module:spring-boot-cache"))
|
||||
implementation(project(path: ":module:spring-boot-cache-test"))
|
||||
implementation(project(path: ":module:spring-boot-data-cassandra"))
|
||||
|
||||
+82
@@ -213,3 +213,85 @@ You can also customize the javadoc:org.springframework.core.retry.RetryPolicy[]
|
||||
IMPORTANT: By default, if retries are disabled and the listener throws an exception, the delivery is retried indefinitely.
|
||||
You can modify this behavior in two ways: Set the `defaultRequeueRejected` property to `false` so that zero re-deliveries are attempted or throw an javadoc:org.springframework.amqp.AmqpRejectAndDontRequeueException[] to signal the message should be rejected.
|
||||
The latter is the mechanism used when retries are enabled and the maximum number of delivery attempts is reached.
|
||||
|
||||
|
||||
|
||||
[[messaging.amqp.rabbitmq-amqp]]
|
||||
== RabbitMQ AMQP 1.0 Support
|
||||
|
||||
https://www.rabbitmq.com/[RabbitMQ] supports the AMQP 1.0 protocol via the https://github.com/rabbitmq/rabbitmq-amqp-java-client[RabbitMQ AMQP 1.0 Java client].
|
||||
Spring AMQP provides support for this client through `spring-rabbitmq-client`.
|
||||
Spring Boot offers auto-configuration for this support via the `spring-boot-starter-amqp-rabbitmq` starter.
|
||||
|
||||
RabbitMQ AMQP 1.0 configuration is controlled by external configuration properties in `+spring.amqp.rabbitmq.*+`.
|
||||
For example, you might declare the following section in `application.properties`:
|
||||
|
||||
[configprops,yaml]
|
||||
----
|
||||
spring:
|
||||
amqp:
|
||||
rabbitmq:
|
||||
host: "localhost"
|
||||
port: 5672
|
||||
username: "admin"
|
||||
password: "secret"
|
||||
----
|
||||
|
||||
Alternatively, you could configure the same connection using the `address` attribute:
|
||||
|
||||
[configprops,yaml]
|
||||
----
|
||||
spring:
|
||||
amqp:
|
||||
rabbitmq:
|
||||
address: "amqp://admin:secret@localhost"
|
||||
----
|
||||
|
||||
NOTE: When specifying an address that way, the `host` and `port` properties are ignored.
|
||||
|
||||
See javadoc:org.springframework.boot.amqp.autoconfigure.RabbitAmqpProperties[] for more of the supported property-based configuration options.
|
||||
To configure lower-level details of the auto-configured javadoc:com.rabbitmq.client.amqp.Environment[], define a javadoc:org.springframework.boot.amqp.autoconfigure.RabbitAmqpEnvironmentBuilderCustomizer[] bean.
|
||||
|
||||
If a javadoc:com.rabbitmq.client.amqp.CredentialsProvider[] bean exists in the context, it will be automatically used by the auto-configured javadoc:com.rabbitmq.client.amqp.Environment[].
|
||||
|
||||
|
||||
|
||||
[[messaging.amqp.rabbitmq-amqp.sending]]
|
||||
=== Sending a Message
|
||||
|
||||
Spring's javadoc:org.springframework.amqp.rabbitmq.client.RabbitAmqpTemplate[] and javadoc:org.springframework.amqp.rabbitmq.client.RabbitAmqpAdmin[] are auto-configured, and you can autowire them directly into your own beans, as shown in the following example:
|
||||
|
||||
include-code::MyBean[]
|
||||
|
||||
If a javadoc:org.springframework.amqp.support.converter.MessageConverter[] bean is defined, it is associated automatically to the auto-configured javadoc:org.springframework.amqp.rabbitmq.client.RabbitAmqpTemplate[].
|
||||
|
||||
You can set properties for the template as follows:
|
||||
|
||||
[configprops,yaml]
|
||||
----
|
||||
spring:
|
||||
amqp:
|
||||
rabbitmq:
|
||||
template:
|
||||
exchange: "my-exchange"
|
||||
routing-key: "my-key"
|
||||
default-receive-queue: "my-queue"
|
||||
----
|
||||
|
||||
You can also customize the javadoc:org.springframework.amqp.rabbitmq.client.RabbitAmqpTemplate[] programmatically by declaring a javadoc:org.springframework.boot.amqp.autoconfigure.RabbitAmqpTemplateCustomizer[] bean.
|
||||
|
||||
|
||||
|
||||
[[messaging.amqp.rabbitmq-amqp.receiving]]
|
||||
=== Receiving a Message
|
||||
|
||||
When the RabbitMQ AMQP 1.0 infrastructure is present, any bean can be annotated with javadoc:org.springframework.amqp.rabbit.annotation.RabbitListener[format=annotation] to create a listener endpoint.
|
||||
If no javadoc:org.springframework.amqp.rabbitmq.client.config.RabbitAmqpListenerContainerFactory[] has been defined, a default one is automatically configured.
|
||||
|
||||
The following sample component creates a listener endpoint on the `someQueue` queue:
|
||||
|
||||
include-code::MyBean[]
|
||||
|
||||
TIP: See javadoc:org.springframework.amqp.rabbit.annotation.EnableRabbit[format=annotation] for more details.
|
||||
|
||||
To customize the listener container, define a javadoc:org.springframework.amqp.rabbit.config.ContainerCustomizer[] bean parameterized with javadoc:org.springframework.amqp.rabbitmq.client.listener.RabbitAmqpListenerContainer[].
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.docs.messaging.amqp.rabbitmqamqp.receiving;
|
||||
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RabbitListener(queues = "someQueue")
|
||||
public class MyBean {
|
||||
|
||||
@RabbitHandler
|
||||
public void processMessage(String content) {
|
||||
// ...
|
||||
}
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.docs.messaging.amqp.rabbitmqamqp.sending;
|
||||
|
||||
import org.springframework.amqp.rabbitmq.client.RabbitAmqpAdmin;
|
||||
import org.springframework.amqp.rabbitmq.client.RabbitAmqpTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class MyBean {
|
||||
|
||||
private final RabbitAmqpAdmin amqpAdmin;
|
||||
|
||||
private final RabbitAmqpTemplate amqpTemplate;
|
||||
|
||||
public MyBean(RabbitAmqpAdmin amqpAdmin, RabbitAmqpTemplate amqpTemplate) {
|
||||
this.amqpAdmin = amqpAdmin;
|
||||
this.amqpTemplate = amqpTemplate;
|
||||
}
|
||||
|
||||
// @fold:on // ...
|
||||
public void someOtherMethod() {
|
||||
this.amqpTemplate.convertAndSend("hello");
|
||||
}
|
||||
// @fold:off
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id "java-library"
|
||||
id "org.springframework.boot.docker-test"
|
||||
id "org.springframework.boot.auto-configuration"
|
||||
id "org.springframework.boot.configuration-properties"
|
||||
id "org.springframework.boot.deployed"
|
||||
id "org.springframework.boot.optional-dependencies"
|
||||
}
|
||||
|
||||
description = "Spring Boot AMQP"
|
||||
|
||||
dependencies {
|
||||
api(project(":core:spring-boot"))
|
||||
api("org.springframework:spring-messaging")
|
||||
api("org.springframework.amqp:spring-rabbitmq-client")
|
||||
|
||||
compileOnly("com.fasterxml.jackson.core:jackson-annotations")
|
||||
|
||||
implementation(project(":module:spring-boot-transaction"))
|
||||
|
||||
optional(project(":core:spring-boot-autoconfigure"))
|
||||
optional(project(":core:spring-boot-docker-compose"))
|
||||
optional(project(":core:spring-boot-testcontainers"))
|
||||
optional(project(":module:spring-boot-health"))
|
||||
optional(project(":module:spring-boot-micrometer-metrics"))
|
||||
optional("io.micrometer:micrometer-core")
|
||||
optional("org.springframework.amqp:spring-rabbit-stream")
|
||||
optional("org.testcontainers:testcontainers-rabbitmq")
|
||||
|
||||
dockerTestImplementation(project(":test-support:spring-boot-docker-test-support"))
|
||||
dockerTestImplementation(testFixtures(project(":core:spring-boot-docker-compose")))
|
||||
dockerTestImplementation("ch.qos.logback:logback-classic")
|
||||
dockerTestImplementation("org.testcontainers:testcontainers-junit-jupiter")
|
||||
|
||||
testCompileOnly("com.fasterxml.jackson.core:jackson-annotations")
|
||||
|
||||
testImplementation(project(":core:spring-boot-test"))
|
||||
testImplementation(project(":test-support:spring-boot-test-support"))
|
||||
|
||||
testRuntimeOnly("ch.qos.logback:logback-classic")
|
||||
}
|
||||
|
||||
tasks.named("compileTestJava") {
|
||||
options.nullability.checking = "tests"
|
||||
}
|
||||
|
||||
tasks.named("compileDockerTestJava") {
|
||||
options.nullability.checking = "tests"
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.amqp.docker.compose;
|
||||
|
||||
import org.springframework.boot.amqp.autoconfigure.RabbitAmqpConnectionDetails;
|
||||
import org.springframework.boot.amqp.autoconfigure.RabbitAmqpConnectionDetails.Address;
|
||||
import org.springframework.boot.docker.compose.service.connection.test.DockerComposeTest;
|
||||
import org.springframework.boot.testsupport.container.TestImage;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link RabbitDockerComposeConnectionDetailsFactory}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @author Scott Frederick
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
class RabbitDockerComposeConnectionDetailsFactoryIntegrationTests {
|
||||
|
||||
@DockerComposeTest(composeFile = "rabbit-compose.yaml", image = TestImage.RABBITMQ)
|
||||
void runCreatesConnectionDetails(RabbitAmqpConnectionDetails connectionDetails) {
|
||||
assertThat(connectionDetails.getUsername()).isEqualTo("myuser");
|
||||
assertThat(connectionDetails.getPassword()).isEqualTo("secret");
|
||||
assertThat(connectionDetails.getVirtualHost()).isEqualTo("/");
|
||||
assertThat(connectionDetails.getAddress()).isNotNull();
|
||||
Address address = connectionDetails.getAddress();
|
||||
assertThat(address.host()).isNotNull();
|
||||
assertThat(address.port()).isGreaterThan(0);
|
||||
}
|
||||
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.amqp.testcontainers;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import org.testcontainers.rabbitmq.RabbitMQContainer;
|
||||
|
||||
import org.springframework.amqp.rabbit.annotation.Queue;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.amqp.rabbitmq.client.RabbitAmqpTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.amqp.autoconfigure.RabbitAmqpAutoConfiguration;
|
||||
import org.springframework.boot.amqp.autoconfigure.RabbitAmqpConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.container.TestImage;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link RabbitContainerConnectionDetailsFactory}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@Testcontainers(disabledWithoutDocker = true)
|
||||
class RabbitContainerConnectionDetailsFactoryIntegrationTests {
|
||||
|
||||
@Container
|
||||
@ServiceConnection
|
||||
static final RabbitMQContainer rabbit = TestImage.container(RabbitMQContainer.class);
|
||||
|
||||
@Autowired(required = false)
|
||||
private RabbitAmqpConnectionDetails connectionDetails;
|
||||
|
||||
@Autowired
|
||||
private RabbitAmqpTemplate rabbitAmqpTemplate;
|
||||
|
||||
@Autowired
|
||||
private TestListener listener;
|
||||
|
||||
@Test
|
||||
void connectionCanBeMadeToRabbitContainer() {
|
||||
assertThat(this.connectionDetails).isNotNull();
|
||||
this.rabbitAmqpTemplate.convertAndSend("test", "message");
|
||||
Awaitility.waitAtMost(Duration.ofMinutes(4))
|
||||
.untilAsserted(() -> assertThat(this.listener.messages).containsExactly("message"));
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ImportAutoConfiguration(RabbitAmqpAutoConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
TestListener testListener() {
|
||||
return new TestListener();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestListener {
|
||||
|
||||
private final List<String> messages = new ArrayList<>();
|
||||
|
||||
@RabbitListener(queuesToDeclare = @Queue("test"))
|
||||
void processMessage(String message) {
|
||||
this.messages.add(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<include resource="org/springframework/boot/logging/logback/base.xml"/>
|
||||
</configuration>
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
services:
|
||||
rabbitmq:
|
||||
image: '{imageName}'
|
||||
environment:
|
||||
- 'RABBITMQ_DEFAULT_USER=myuser'
|
||||
- 'RABBITMQ_DEFAULT_PASS=secret'
|
||||
ports:
|
||||
- '5672'
|
||||
@@ -0,0 +1 @@
|
||||
spring.test.context.cache.maxSize=1
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.amqp.autoconfigure;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Adapts {@link RabbitAmqpProperties} to {@link RabbitAmqpConnectionDetails}.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
class PropertiesRabbitAmqpConnectionDetails implements RabbitAmqpConnectionDetails {
|
||||
|
||||
private final RabbitAmqpProperties properties;
|
||||
|
||||
PropertiesRabbitAmqpConnectionDetails(RabbitAmqpProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.properties.determineUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getPassword() {
|
||||
return this.properties.determinePassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getVirtualHost() {
|
||||
return this.properties.determineVirtualHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getAddress() {
|
||||
String address = this.properties.determineAddress();
|
||||
int portSeparatorIndex = address.lastIndexOf(':');
|
||||
String host = address.substring(0, portSeparatorIndex);
|
||||
String port = address.substring(portSeparatorIndex + 1);
|
||||
return new Address(host, Integer.parseInt(port));
|
||||
}
|
||||
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.amqp.autoconfigure;
|
||||
|
||||
import com.rabbitmq.client.amqp.Connection;
|
||||
import com.rabbitmq.client.amqp.CredentialsProvider;
|
||||
import com.rabbitmq.client.amqp.Environment;
|
||||
import com.rabbitmq.client.amqp.impl.AmqpEnvironmentBuilder;
|
||||
import com.rabbitmq.client.amqp.impl.AmqpEnvironmentBuilder.EnvironmentConnectionSettings;
|
||||
|
||||
import org.springframework.amqp.rabbit.config.ContainerCustomizer;
|
||||
import org.springframework.amqp.rabbitmq.client.AmqpConnectionFactory;
|
||||
import org.springframework.amqp.rabbitmq.client.RabbitAmqpAdmin;
|
||||
import org.springframework.amqp.rabbitmq.client.RabbitAmqpTemplate;
|
||||
import org.springframework.amqp.rabbitmq.client.SingleAmqpConnectionFactory;
|
||||
import org.springframework.amqp.rabbitmq.client.config.RabbitAmqpListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbitmq.client.listener.RabbitAmqpListenerContainer;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.amqp.autoconfigure.RabbitAmqpConnectionDetails.Address;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for {@link RabbitAmqpTemplate}.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @since 4.1.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass({ RabbitAmqpTemplate.class, Connection.class })
|
||||
@EnableConfigurationProperties(RabbitAmqpProperties.class)
|
||||
@Import(RabbitAnnotationDrivenConfiguration.class)
|
||||
public final class RabbitAmqpAutoConfiguration {
|
||||
|
||||
private final RabbitAmqpProperties properties;
|
||||
|
||||
RabbitAmqpAutoConfiguration(RabbitAmqpProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
RabbitAmqpConnectionDetails rabbitConnectionDetails() {
|
||||
return new PropertiesRabbitAmqpConnectionDetails(this.properties);
|
||||
}
|
||||
|
||||
@Bean(name = "rabbitListenerContainerFactory")
|
||||
@ConditionalOnMissingBean(name = "rabbitListenerContainerFactory")
|
||||
RabbitAmqpListenerContainerFactory rabbitAmqpListenerContainerFactory(AmqpConnectionFactory connectionFactory,
|
||||
ObjectProvider<ContainerCustomizer<RabbitAmqpListenerContainer>> amqpContainerCustomizer) {
|
||||
RabbitAmqpListenerContainerFactory factory = new RabbitAmqpListenerContainerFactory(connectionFactory);
|
||||
amqpContainerCustomizer.ifUnique(factory::setContainerCustomizer);
|
||||
|
||||
RabbitAmqpProperties.AmqpContainer configuration = this.properties.getListener().getAmqp();
|
||||
factory.setObservationEnabled(configuration.isObservationEnabled());
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
Environment rabbitAmqpEnvironment(RabbitAmqpConnectionDetails connectionDetails,
|
||||
ObjectProvider<RabbitAmqpEnvironmentBuilderCustomizer> customizers,
|
||||
ObjectProvider<CredentialsProvider> credentialsProvider) {
|
||||
PropertyMapper map = PropertyMapper.get();
|
||||
EnvironmentConnectionSettings environmentConnectionSettings = new AmqpEnvironmentBuilder().connectionSettings();
|
||||
Address address = connectionDetails.getAddress();
|
||||
map.from(address::host).to(environmentConnectionSettings::host);
|
||||
map.from(address::port).to(environmentConnectionSettings::port);
|
||||
map.from(connectionDetails::getUsername).to(environmentConnectionSettings::username);
|
||||
map.from(connectionDetails::getPassword).to(environmentConnectionSettings::password);
|
||||
map.from(connectionDetails::getVirtualHost).to(environmentConnectionSettings::virtualHost);
|
||||
map.from(credentialsProvider::getIfAvailable).to(environmentConnectionSettings::credentialsProvider);
|
||||
|
||||
AmqpEnvironmentBuilder builder = environmentConnectionSettings.environmentBuilder();
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
AmqpConnectionFactory amqpConnectionFactory(Environment environment) {
|
||||
return new SingleAmqpConnectionFactory(environment);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
RabbitAmqpTemplate rabbitAmqpTemplate(AmqpConnectionFactory connectionFactory,
|
||||
ObjectProvider<RabbitAmqpTemplateCustomizer> customizers,
|
||||
ObjectProvider<MessageConverter> messageConverter) {
|
||||
RabbitAmqpTemplate rabbitAmqpTemplate = new RabbitAmqpTemplate(connectionFactory);
|
||||
if (messageConverter.getIfAvailable() != null) {
|
||||
rabbitAmqpTemplate.setMessageConverter(messageConverter.getIfAvailable());
|
||||
}
|
||||
RabbitAmqpProperties.Template templateProperties = this.properties.getTemplate();
|
||||
|
||||
PropertyMapper map = PropertyMapper.get();
|
||||
map.from(templateProperties::getDefaultReceiveQueue).to(rabbitAmqpTemplate::setReceiveQueue);
|
||||
map.from(templateProperties::getExchange).to(rabbitAmqpTemplate::setExchange);
|
||||
map.from(templateProperties::getRoutingKey).to(rabbitAmqpTemplate::setRoutingKey);
|
||||
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(rabbitAmqpTemplate));
|
||||
return rabbitAmqpTemplate;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
RabbitAmqpAdmin rabbitAmqpAdmin(AmqpConnectionFactory connectionFactory) {
|
||||
return new RabbitAmqpAdmin(connectionFactory);
|
||||
}
|
||||
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.amqp.autoconfigure;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to a RabbitMQ AMQP service.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public interface RabbitAmqpConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* Login user to authenticate to the broker.
|
||||
* @return the login user to authenticate to the broker or {@code null}
|
||||
*/
|
||||
default @Nullable String getUsername() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login to authenticate against the broker.
|
||||
* @return the login to authenticate against the broker or {@code null}
|
||||
*/
|
||||
default @Nullable String getPassword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Virtual host to use when connecting to the broker.
|
||||
* @return the virtual host to use when connecting to the broker or {@code null}
|
||||
*/
|
||||
default @Nullable String getVirtualHost() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address.
|
||||
* @return the address
|
||||
* @throws IllegalStateException if the address list is empty
|
||||
*/
|
||||
Address getAddress();
|
||||
|
||||
/**
|
||||
* A RabbitMQ address.
|
||||
*
|
||||
* @param host the host
|
||||
* @param port the port
|
||||
*/
|
||||
record Address(String host, int port) {
|
||||
}
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.amqp.autoconfigure;
|
||||
|
||||
import com.rabbitmq.client.amqp.Environment;
|
||||
import com.rabbitmq.client.amqp.impl.AmqpEnvironmentBuilder;
|
||||
|
||||
/**
|
||||
* Callback interface that can be implemented by beans wishing to customize the
|
||||
* auto-configured {@link Environment} that is created by an
|
||||
* {@link AmqpEnvironmentBuilder}.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @since 4.1.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RabbitAmqpEnvironmentBuilderCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the {@code AmqpEnvironmentBuilder}.
|
||||
* @param builder the builder to customize
|
||||
*/
|
||||
void customize(AmqpEnvironmentBuilder builder);
|
||||
|
||||
}
|
||||
+383
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.amqp.autoconfigure;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Configuration properties for Rabbit AMQP.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @since 4.1.0
|
||||
*/
|
||||
@ConfigurationProperties("spring.amqp.rabbitmq")
|
||||
public class RabbitAmqpProperties {
|
||||
|
||||
private static final int DEFAULT_PORT = 5672;
|
||||
|
||||
/**
|
||||
* RabbitMQ host. Ignored if an address is set.
|
||||
*/
|
||||
private String host = "localhost";
|
||||
|
||||
/**
|
||||
* RabbitMQ port. Ignored if an address is set. Default to 5672, or 5671 if SSL is
|
||||
* enabled.
|
||||
*/
|
||||
private @Nullable Integer port;
|
||||
|
||||
/**
|
||||
* Login user to authenticate to the broker.
|
||||
*/
|
||||
private String username = "guest";
|
||||
|
||||
/**
|
||||
* Login to authenticate against the broker.
|
||||
*/
|
||||
private String password = "guest";
|
||||
|
||||
/**
|
||||
* Virtual host to use when connecting to the broker.
|
||||
*/
|
||||
private @Nullable String virtualHost;
|
||||
|
||||
/**
|
||||
* The address to which the client should connect. When set, the host and port are
|
||||
* ignored.
|
||||
*/
|
||||
private @Nullable String address;
|
||||
|
||||
/**
|
||||
* Listener container configuration.
|
||||
*/
|
||||
private final Listener listener = new Listener();
|
||||
|
||||
private final Template template = new Template();
|
||||
|
||||
private @Nullable Address parsedAddress;
|
||||
|
||||
public String getHost() {
|
||||
return this.host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public @Nullable Integer getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port from the address, or the configured port if no address have been
|
||||
* set.
|
||||
* @return the port
|
||||
* @see #setAddress(String)
|
||||
* @see #getPort()
|
||||
*/
|
||||
public int determinePort() {
|
||||
if (this.parsedAddress == null) {
|
||||
Integer port = getPort();
|
||||
if (port != null) {
|
||||
return port;
|
||||
}
|
||||
return DEFAULT_PORT;
|
||||
}
|
||||
return this.parsedAddress.port;
|
||||
}
|
||||
|
||||
public void setPort(@Nullable Integer port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public @Nullable String getAddress() {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured address ({@code host:port}) created from the configured host
|
||||
* and port.
|
||||
* @return the address
|
||||
*/
|
||||
public String determineAddress() {
|
||||
if (this.parsedAddress == null) {
|
||||
if (this.host.contains(",")) {
|
||||
throw new InvalidConfigurationPropertyValueException("spring.amqp.host", this.host,
|
||||
"Invalid character ','. Value must be a single host. For multiple hosts, use property 'spring.amqp.address' instead.");
|
||||
}
|
||||
return this.host + ":" + determinePort();
|
||||
}
|
||||
return this.parsedAddress.host + ":" + this.parsedAddress.port;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
this.parsedAddress = parseAddress(address);
|
||||
}
|
||||
|
||||
private Address parseAddress(String address) {
|
||||
return new Address(address);
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
/**
|
||||
* If address has been set and has a username it is returned. Otherwise returns the
|
||||
* result of calling {@code getUsername()}.
|
||||
* @return the username
|
||||
* @see #setAddress(String)
|
||||
* @see #getUsername()
|
||||
*/
|
||||
public String determineUsername() {
|
||||
if (this.parsedAddress == null) {
|
||||
return this.username;
|
||||
}
|
||||
Address address = this.parsedAddress;
|
||||
return (address.username != null) ? address.username : this.username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
/**
|
||||
* If address has been set and has a password it is returned. Otherwise returns the
|
||||
* result of calling {@code getPassword()}.
|
||||
* @return the password or {@code null}
|
||||
* @see #setAddress(String)
|
||||
* @see #getPassword()
|
||||
*/
|
||||
public @Nullable String determinePassword() {
|
||||
if (this.parsedAddress == null) {
|
||||
return getPassword();
|
||||
}
|
||||
Address address = this.parsedAddress;
|
||||
return (address.password != null) ? address.password : getPassword();
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public @Nullable String getVirtualHost() {
|
||||
return this.virtualHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* If address has been set and has a virtual host it is returned. Otherwise returns
|
||||
* the result of calling {@code getVirtualHost()}.
|
||||
* @return the virtual host or {@code null}
|
||||
* @see #setAddress(String)
|
||||
* @see #getVirtualHost()
|
||||
*/
|
||||
public @Nullable String determineVirtualHost() {
|
||||
if (this.parsedAddress == null) {
|
||||
return getVirtualHost();
|
||||
}
|
||||
Address address = this.parsedAddress;
|
||||
return (address.virtualHost != null) ? address.virtualHost : getVirtualHost();
|
||||
}
|
||||
|
||||
public void setVirtualHost(@Nullable String virtualHost) {
|
||||
this.virtualHost = StringUtils.hasText(virtualHost) ? virtualHost : "/";
|
||||
}
|
||||
|
||||
public Listener getListener() {
|
||||
return this.listener;
|
||||
}
|
||||
|
||||
public Template getTemplate() {
|
||||
return this.template;
|
||||
}
|
||||
|
||||
public static class Listener {
|
||||
|
||||
private final AmqpContainer amqp = new AmqpContainer();
|
||||
|
||||
public AmqpContainer getAmqp() {
|
||||
return this.amqp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration properties for {@code RabbitAmqpListenerContainer}.
|
||||
*/
|
||||
public static class AmqpContainer {
|
||||
|
||||
/**
|
||||
* Whether to enable observation.
|
||||
*/
|
||||
private boolean observationEnabled;
|
||||
|
||||
/**
|
||||
* Batch size, expressed as the number of physical messages, to be used by the
|
||||
* container.
|
||||
*/
|
||||
private @Nullable Integer batchSize;
|
||||
|
||||
public boolean isObservationEnabled() {
|
||||
return this.observationEnabled;
|
||||
}
|
||||
|
||||
public void setObservationEnabled(boolean observationEnabled) {
|
||||
this.observationEnabled = observationEnabled;
|
||||
}
|
||||
|
||||
public @Nullable Integer getBatchSize() {
|
||||
return this.batchSize;
|
||||
}
|
||||
|
||||
public void setBatchSize(@Nullable Integer batchSize) {
|
||||
this.batchSize = batchSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Template {
|
||||
|
||||
/**
|
||||
* Name of the default exchange to use for send operations.
|
||||
*/
|
||||
private String exchange = "";
|
||||
|
||||
/**
|
||||
* Value of a default routing key to use for send operations.
|
||||
*/
|
||||
private String routingKey = "";
|
||||
|
||||
/**
|
||||
* Name of the default queue to receive messages from when none is specified
|
||||
* explicitly.
|
||||
*/
|
||||
private @Nullable String defaultReceiveQueue;
|
||||
|
||||
public String getExchange() {
|
||||
return this.exchange;
|
||||
}
|
||||
|
||||
public void setExchange(String exchange) {
|
||||
this.exchange = exchange;
|
||||
}
|
||||
|
||||
public String getRoutingKey() {
|
||||
return this.routingKey;
|
||||
}
|
||||
|
||||
public void setRoutingKey(String routingKey) {
|
||||
this.routingKey = routingKey;
|
||||
}
|
||||
|
||||
public @Nullable String getDefaultReceiveQueue() {
|
||||
return this.defaultReceiveQueue;
|
||||
}
|
||||
|
||||
public void setDefaultReceiveQueue(@Nullable String defaultReceiveQueue) {
|
||||
this.defaultReceiveQueue = defaultReceiveQueue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class Address {
|
||||
|
||||
private static final String PREFIX_AMQP = "amqp://";
|
||||
|
||||
private static final String PREFIX_AMQP_SECURE = "amqps://";
|
||||
|
||||
private String host;
|
||||
|
||||
private int port;
|
||||
|
||||
private @Nullable String username;
|
||||
|
||||
private @Nullable String password;
|
||||
|
||||
private @Nullable 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_SECURE)) {
|
||||
return input.substring(PREFIX_AMQP_SECURE.length());
|
||||
}
|
||||
if (input.startsWith(PREFIX_AMQP)) {
|
||||
return input.substring(PREFIX_AMQP.length());
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
private String parseUsernameAndPassword(String input) {
|
||||
String[] splitInput = StringUtils.split(input, "@");
|
||||
if (splitInput == null) {
|
||||
return input;
|
||||
}
|
||||
String credentials = splitInput[0];
|
||||
String[] splitCredentials = StringUtils.split(credentials, ":");
|
||||
if (splitCredentials == null) {
|
||||
this.username = credentials;
|
||||
}
|
||||
else {
|
||||
this.username = splitCredentials[0];
|
||||
this.password = splitCredentials[1];
|
||||
}
|
||||
return splitInput[1];
|
||||
}
|
||||
|
||||
private String parseVirtualHost(String input) {
|
||||
int hostIndex = input.indexOf('/');
|
||||
if (hostIndex >= 0) {
|
||||
this.virtualHost = input.substring(hostIndex + 1);
|
||||
if (this.virtualHost.isEmpty()) {
|
||||
this.virtualHost = "/";
|
||||
}
|
||||
input = input.substring(0, hostIndex);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
private void parseHostAndPort(String input) {
|
||||
int bracketIndex = input.lastIndexOf(']');
|
||||
int colonIndex = input.lastIndexOf(':');
|
||||
if (colonIndex == -1 || colonIndex < bracketIndex) {
|
||||
this.host = input;
|
||||
this.port = DEFAULT_PORT;
|
||||
}
|
||||
else {
|
||||
this.host = input.substring(0, colonIndex);
|
||||
this.port = Integer.parseInt(input.substring(colonIndex + 1));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.amqp.autoconfigure;
|
||||
|
||||
import org.springframework.amqp.rabbitmq.client.RabbitAmqpTemplate;
|
||||
|
||||
/**
|
||||
* Callback interface that can be used to customize a {@link RabbitAmqpTemplate}.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RabbitAmqpTemplateCustomizer {
|
||||
|
||||
/**
|
||||
* Callback to customize a {@link RabbitAmqpTemplate} instance.
|
||||
* @param rabbitAmqpTemplate the rabbitAmqpTemplate to customize
|
||||
*/
|
||||
void customize(RabbitAmqpTemplate rabbitAmqpTemplate);
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.amqp.autoconfigure;
|
||||
|
||||
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
|
||||
import org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configuration for Spring AMQP annotation driven endpoints.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(EnableRabbit.class)
|
||||
class RabbitAnnotationDrivenConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableRabbit
|
||||
@ConditionalOnMissingBean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
|
||||
static class EnableRabbitConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for RabbitMQ.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.amqp.autoconfigure;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.amqp.docker.compose;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.amqp.autoconfigure.RabbitAmqpConnectionDetails;
|
||||
import org.springframework.boot.docker.compose.core.RunningService;
|
||||
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory;
|
||||
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource;
|
||||
|
||||
/**
|
||||
* {@link DockerComposeConnectionDetailsFactory} to create
|
||||
* {@link RabbitAmqpConnectionDetails} for a {@code rabbitmq} service.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
class RabbitDockerComposeConnectionDetailsFactory
|
||||
extends DockerComposeConnectionDetailsFactory<RabbitAmqpConnectionDetails> {
|
||||
|
||||
private static final int RABBITMQ_PORT = 5672;
|
||||
|
||||
protected RabbitDockerComposeConnectionDetailsFactory() {
|
||||
super("rabbitmq");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable RabbitAmqpConnectionDetails getDockerComposeConnectionDetails(
|
||||
DockerComposeConnectionSource source) {
|
||||
try {
|
||||
return new RabbitAmqpDockerComposeConnectionDetails(source.getRunningService());
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link RabbitAmqpConnectionDetails} backed by a {@code rabbitmq}
|
||||
* {@link RunningService}.
|
||||
*/
|
||||
static class RabbitAmqpDockerComposeConnectionDetails extends DockerComposeConnectionDetails
|
||||
implements RabbitAmqpConnectionDetails {
|
||||
|
||||
private final RabbitEnvironment environment;
|
||||
|
||||
private final Address address;
|
||||
|
||||
protected RabbitAmqpDockerComposeConnectionDetails(RunningService service) {
|
||||
super(service);
|
||||
this.environment = new RabbitEnvironment(service.env());
|
||||
this.address = new Address(service.host(), service.ports().get(RABBITMQ_PORT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getUsername() {
|
||||
return this.environment.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getPassword() {
|
||||
return this.environment.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVirtualHost() {
|
||||
return "/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getAddress() {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.amqp.docker.compose;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* RabbitMQ environment details.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class RabbitEnvironment {
|
||||
|
||||
private final @Nullable String username;
|
||||
|
||||
private final @Nullable String password;
|
||||
|
||||
RabbitEnvironment(Map<String, @Nullable String> env) {
|
||||
this.username = env.getOrDefault("RABBITMQ_DEFAULT_USER", env.getOrDefault("RABBITMQ_USERNAME", "guest"));
|
||||
this.password = env.getOrDefault("RABBITMQ_DEFAULT_PASS", env.getOrDefault("RABBITMQ_PASSWORD", "guest"));
|
||||
}
|
||||
|
||||
@Nullable String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
@Nullable String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Support for Docker Compose RabbitMQ service connections.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.amqp.docker.compose;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.amqp.testcontainers;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.testcontainers.rabbitmq.RabbitMQContainer;
|
||||
|
||||
import org.springframework.boot.amqp.autoconfigure.RabbitAmqpConnectionDetails;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link RabbitAmqpConnectionDetails}
|
||||
* from a {@link ServiceConnection @ServiceConnection}-annotated
|
||||
* {@link RabbitMQContainer}.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
class RabbitContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<RabbitMQContainer, RabbitAmqpConnectionDetails> {
|
||||
|
||||
@Override
|
||||
protected RabbitAmqpConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<RabbitMQContainer> source) {
|
||||
return new RabbitAmqpMqContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link RabbitAmqpConnectionDetails} backed by a {@link ContainerConnectionSource}.
|
||||
*/
|
||||
static final class RabbitAmqpMqContainerConnectionDetails extends ContainerConnectionDetails<RabbitMQContainer>
|
||||
implements RabbitAmqpConnectionDetails {
|
||||
|
||||
private RabbitAmqpMqContainerConnectionDetails(ContainerConnectionSource<RabbitMQContainer> source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return getContainer().getAdminUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return getContainer().getAdminPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getAddress() {
|
||||
URI uri = URI.create((getSslBundle() != null) ? getContainer().getAmqpsUrl() : getContainer().getAmqpUrl());
|
||||
return new Address(uri.getHost(), uri.getPort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable SslBundle getSslBundle() {
|
||||
return super.getSslBundle();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Support for testcontainers RabbitMQ service connections.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.amqp.testcontainers;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -0,0 +1,4 @@
|
||||
# Connection Details Factories
|
||||
org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory=\
|
||||
org.springframework.boot.amqp.docker.compose.RabbitDockerComposeConnectionDetailsFactory,\
|
||||
org.springframework.boot.amqp.testcontainers.RabbitContainerConnectionDetailsFactory
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.springframework.boot.amqp.autoconfigure.RabbitAmqpAutoConfiguration
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.amqp.autoconfigure;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.amqp.rabbit.config.ContainerCustomizer;
|
||||
import org.springframework.amqp.rabbitmq.client.AmqpConnectionFactory;
|
||||
import org.springframework.amqp.rabbitmq.client.RabbitAmqpAdmin;
|
||||
import org.springframework.amqp.rabbitmq.client.RabbitAmqpTemplate;
|
||||
import org.springframework.amqp.rabbitmq.client.config.RabbitAmqpListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbitmq.client.listener.RabbitAmqpListenerContainer;
|
||||
import org.springframework.amqp.support.converter.MessageConversionException;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link RabbitAmqpAutoConfiguration}.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
class RabbitAmqpAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(RabbitAmqpAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void testDefaultRabbitAmqpConfiguration() {
|
||||
this.contextRunner.run((context) -> {
|
||||
RabbitAmqpListenerContainerFactory listenerContainerFactory = context
|
||||
.getBean(RabbitAmqpListenerContainerFactory.class);
|
||||
AmqpConnectionFactory connectionFactory = context.getBean(AmqpConnectionFactory.class);
|
||||
RabbitAmqpTemplate rabbitAmqpTemplate = context.getBean(RabbitAmqpTemplate.class);
|
||||
RabbitAmqpAdmin rabbitAmqpAdmin = context.getBean(RabbitAmqpAdmin.class);
|
||||
|
||||
assertThat(listenerContainerFactory).isNotNull();
|
||||
assertThat(listenerContainerFactory).extracting("containerCustomizer").isNull();
|
||||
assertThat(connectionFactory).isNotNull();
|
||||
assertThat(rabbitAmqpTemplate).isNotNull();
|
||||
assertThat(rabbitAmqpAdmin).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenMultipleRabbitAmqpTemplateCustomizersAreDefinedThenTheyAreCalledInOrder() {
|
||||
this.contextRunner.withUserConfiguration(MultipleRabbitAmqpTemplateCustomizersConfiguration.class)
|
||||
.run((context) -> {
|
||||
RabbitAmqpTemplateCustomizer firstCustomizer = context.getBean("firstCustomizer",
|
||||
RabbitAmqpTemplateCustomizer.class);
|
||||
RabbitAmqpTemplateCustomizer secondCustomizer = context.getBean("secondCustomizer",
|
||||
RabbitAmqpTemplateCustomizer.class);
|
||||
InOrder inOrder = inOrder(firstCustomizer, secondCustomizer);
|
||||
RabbitAmqpTemplate template = context.getBean(RabbitAmqpTemplate.class);
|
||||
then(firstCustomizer).should(inOrder).customize(template);
|
||||
then(secondCustomizer).should(inOrder).customize(template);
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testListenerContainerFactoryWithContainerCustomizer() {
|
||||
this.contextRunner.withUserConfiguration(AmqpContainerCustomizerConfiguration.class).run((context) -> {
|
||||
RabbitAmqpListenerContainerFactory listenerContainerFactory = context
|
||||
.getBean(RabbitAmqpListenerContainerFactory.class);
|
||||
|
||||
assertThat(listenerContainerFactory).isNotNull();
|
||||
assertThat(listenerContainerFactory).extracting("containerCustomizer").isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class MultipleRabbitAmqpTemplateCustomizersConfiguration {
|
||||
|
||||
@Bean
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
RabbitAmqpTemplateCustomizer secondCustomizer() {
|
||||
return mock(RabbitAmqpTemplateCustomizer.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(0)
|
||||
RabbitAmqpTemplateCustomizer firstCustomizer() {
|
||||
return mock(RabbitAmqpTemplateCustomizer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Import(TestListener.class)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class AmqpContainerCustomizerConfiguration {
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("unchecked")
|
||||
ContainerCustomizer<RabbitAmqpListenerContainer> customizer() {
|
||||
return mock(ContainerCustomizer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomMessageConverterConfiguration {
|
||||
|
||||
@Bean
|
||||
MessageConverter messageConverter() {
|
||||
return new MessageConverter() {
|
||||
|
||||
@Override
|
||||
public Message toMessage(Object object, MessageProperties messageProperties)
|
||||
throws MessageConversionException {
|
||||
return new Message(object.toString().getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fromMessage(Message message) throws MessageConversionException {
|
||||
return new String(message.getBody());
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestListener {
|
||||
|
||||
@RabbitListener(queues = "test", autoStartup = "false")
|
||||
void listen(String in) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.amqp.docker.compose;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link RabbitEnvironment}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class RabbitEnvironmentTests {
|
||||
|
||||
@Test
|
||||
void getUsernameWhenNoRabbitmqDefaultUser() {
|
||||
RabbitEnvironment environment = new RabbitEnvironment(Collections.emptyMap());
|
||||
assertThat(environment.getUsername()).isEqualTo("guest");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getUsernameWhenHasRabbitmqDefaultUser() {
|
||||
RabbitEnvironment environment = new RabbitEnvironment(Map.of("RABBITMQ_DEFAULT_USER", "me"));
|
||||
assertThat(environment.getUsername()).isEqualTo("me");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getUsernameWhenHasRabbitmqUsername() {
|
||||
RabbitEnvironment environment = new RabbitEnvironment(Map.of("RABBITMQ_USERNAME", "me"));
|
||||
assertThat(environment.getUsername()).isEqualTo("me");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getUsernameWhenNoRabbitmqDefaultPass() {
|
||||
RabbitEnvironment environment = new RabbitEnvironment(Collections.emptyMap());
|
||||
assertThat(environment.getPassword()).isEqualTo("guest");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getUsernameWhenHasRabbitmqDefaultPass() {
|
||||
RabbitEnvironment environment = new RabbitEnvironment(Map.of("RABBITMQ_DEFAULT_PASS", "secret"));
|
||||
assertThat(environment.getPassword()).isEqualTo("secret");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getUsernameWhenHasRabbitmqPassword() {
|
||||
RabbitEnvironment environment = new RabbitEnvironment(Map.of("RABBITMQ_PASSWORD", "secret"));
|
||||
assertThat(environment.getPassword()).isEqualTo("secret");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2124,6 +2124,7 @@ bom {
|
||||
"spring-boot-actuator",
|
||||
"spring-boot-actuator-autoconfigure",
|
||||
"spring-boot-amqp",
|
||||
"spring-boot-amqp-rabbitmq",
|
||||
"spring-boot-artemis",
|
||||
"spring-boot-autoconfigure",
|
||||
"spring-boot-autoconfigure-classic",
|
||||
@@ -2246,6 +2247,7 @@ bom {
|
||||
"spring-boot-starter-actuator",
|
||||
"spring-boot-starter-actuator-test",
|
||||
"spring-boot-starter-amqp",
|
||||
"spring-boot-starter-amqp-rabbitmq",
|
||||
"spring-boot-starter-amqp-test",
|
||||
"spring-boot-starter-artemis",
|
||||
"spring-boot-starter-artemis-test",
|
||||
|
||||
@@ -81,6 +81,7 @@ include "module:spring-boot-activemq"
|
||||
include "module:spring-boot-actuator"
|
||||
include "module:spring-boot-actuator-autoconfigure"
|
||||
include "module:spring-boot-amqp"
|
||||
include "module:spring-boot-amqp-rabbitmq"
|
||||
include "module:spring-boot-artemis"
|
||||
include "module:spring-boot-autoconfigure-classic"
|
||||
include "module:spring-boot-autoconfigure-classic-modules"
|
||||
@@ -213,6 +214,7 @@ include "starter:spring-boot-starter-activemq-test"
|
||||
include "starter:spring-boot-starter-actuator"
|
||||
include "starter:spring-boot-starter-actuator-test"
|
||||
include "starter:spring-boot-starter-amqp"
|
||||
include "starter:spring-boot-starter-amqp-rabbitmq"
|
||||
include "starter:spring-boot-starter-amqp-test"
|
||||
include "starter:spring-boot-starter-artemis"
|
||||
include "starter:spring-boot-starter-artemis-test"
|
||||
@@ -405,6 +407,7 @@ include ":smoke-test:spring-boot-smoke-test-actuator-log4j2"
|
||||
include ":smoke-test:spring-boot-smoke-test-actuator-noweb"
|
||||
include ":smoke-test:spring-boot-smoke-test-actuator-ui"
|
||||
include ":smoke-test:spring-boot-smoke-test-amqp"
|
||||
include ":smoke-test:spring-boot-smoke-test-amqp-rabbitmq"
|
||||
include ":smoke-test:spring-boot-smoke-test-ant"
|
||||
include ":smoke-test:spring-boot-smoke-test-artemis"
|
||||
include ":smoke-test:spring-boot-smoke-test-aspectj"
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id "java"
|
||||
id "org.springframework.boot.docker-test"
|
||||
}
|
||||
|
||||
description = "Spring Boot RabbitMQ AMQP smoke test"
|
||||
|
||||
dependencies {
|
||||
implementation(project(":starter:spring-boot-starter-amqp-rabbitmq"))
|
||||
|
||||
dockerTestImplementation(project(":starter:spring-boot-starter-test"))
|
||||
dockerTestImplementation(project(":core:spring-boot-testcontainers"))
|
||||
dockerTestImplementation(project(":test-support:spring-boot-docker-test-support"))
|
||||
dockerTestImplementation("org.awaitility:awaitility")
|
||||
dockerTestImplementation("org.testcontainers:testcontainers-junit-jupiter")
|
||||
dockerTestImplementation("org.testcontainers:testcontainers-rabbitmq")
|
||||
}
|
||||
|
||||
tasks.named("compileTestJava") {
|
||||
options.nullability.checking = "tests"
|
||||
}
|
||||
|
||||
tasks.named("compileDockerTestJava") {
|
||||
options.nullability.checking = "tests"
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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 smoketest.amqp;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import org.testcontainers.rabbitmq.RabbitMQContainer;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.system.CapturedOutput;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.container.TestImage;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest
|
||||
@Testcontainers(disabledWithoutDocker = true)
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class SampleRabbitAmqpSimpleApplicationTests {
|
||||
|
||||
@Container
|
||||
@ServiceConnection
|
||||
static final RabbitMQContainer rabbit = TestImage.container(RabbitMQContainer.class);
|
||||
|
||||
@Autowired
|
||||
private Sender sender;
|
||||
|
||||
@Test
|
||||
void sendSimpleMessage(CapturedOutput output) {
|
||||
this.sender.send("Test message");
|
||||
Awaitility.waitAtMost(Duration.ofMinutes(1)).untilAsserted(() -> assertThat(output).contains("Test message"));
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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 smoketest.amqp;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.messaging.handler.annotation.Payload;
|
||||
|
||||
@SpringBootApplication
|
||||
@RabbitListener(queues = "foo")
|
||||
public class SampleRabbitAmqpSimpleApplication {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SampleRabbitAmqpSimpleApplication.class);
|
||||
|
||||
@Bean
|
||||
public Sender mySender() {
|
||||
return new Sender();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue fooQueue() {
|
||||
return new Queue("foo");
|
||||
}
|
||||
|
||||
@RabbitHandler
|
||||
public void process(@Payload String foo) {
|
||||
logger.info(foo);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationRunner runner(Sender sender) {
|
||||
return (args) -> sender.send("Hello");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleRabbitAmqpSimpleApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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 smoketest.amqp;
|
||||
|
||||
import org.springframework.amqp.rabbitmq.client.RabbitAmqpTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
public class Sender {
|
||||
|
||||
@Autowired
|
||||
private RabbitAmqpTemplate rabbitAmqpTemplate;
|
||||
|
||||
public void send(String message) {
|
||||
this.rabbitAmqpTemplate.convertAndSend("foo", message);
|
||||
}
|
||||
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
@NullMarked
|
||||
package smoketest.amqp;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id "org.springframework.boot.starter"
|
||||
}
|
||||
|
||||
description = "Starter for using Spring AMQP with Rabbit MQ over AMQP 1.0 protocol"
|
||||
|
||||
dependencies {
|
||||
api(project(":starter:spring-boot-starter"))
|
||||
|
||||
api(project(":module:spring-boot-amqp-rabbitmq"))
|
||||
}
|
||||
Reference in New Issue
Block a user