From 3f25c06419dc53d3d506f11880cb079e39ec62b8 Mon Sep 17 00:00:00 2001 From: Wander Costa Date: Mon, 1 Mar 2021 06:51:21 +0100 Subject: [PATCH 1/4] GH-35 Initializing version 2.4 --- pom.xml | 2 +- .../spring-multirabbit-example-java/pom.xml | 2 +- .../spring-multirabbit-example-kotlin/pom.xml | 2 +- .../spring-multirabbit-extension-example/pom.xml | 2 +- spring-multirabbit-integration/pom.xml | 2 +- spring-multirabbit/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index b01ba36..18761c8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.free-now.multirabbit spring-multirabbit-parent - 2.3.1-SNAPSHOT + 2.4.0-SNAPSHOT pom Spring MultiRabbit diff --git a/spring-multirabbit-examples/spring-multirabbit-example-java/pom.xml b/spring-multirabbit-examples/spring-multirabbit-example-java/pom.xml index f3834d3..904b7e0 100644 --- a/spring-multirabbit-examples/spring-multirabbit-example-java/pom.xml +++ b/spring-multirabbit-examples/spring-multirabbit-example-java/pom.xml @@ -12,7 +12,7 @@ com.free-now.multirabbit spring-multirabbit-example-java - 2.3.1-SNAPSHOT + 2.4.0-SNAPSHOT jar Spring MultiRabbit Example for Java diff --git a/spring-multirabbit-examples/spring-multirabbit-example-kotlin/pom.xml b/spring-multirabbit-examples/spring-multirabbit-example-kotlin/pom.xml index c52a892..b7aa2c7 100644 --- a/spring-multirabbit-examples/spring-multirabbit-example-kotlin/pom.xml +++ b/spring-multirabbit-examples/spring-multirabbit-example-kotlin/pom.xml @@ -12,7 +12,7 @@ com.free-now.multirabbit spring-multirabbit-example-kotlin - 2.3.1-SNAPSHOT + 2.4.0-SNAPSHOT jar Spring MultiRabbit Example for Kotlin diff --git a/spring-multirabbit-examples/spring-multirabbit-extension-example/pom.xml b/spring-multirabbit-examples/spring-multirabbit-extension-example/pom.xml index 50eda16..eabe13a 100644 --- a/spring-multirabbit-examples/spring-multirabbit-extension-example/pom.xml +++ b/spring-multirabbit-examples/spring-multirabbit-extension-example/pom.xml @@ -12,7 +12,7 @@ com.free-now.multirabbit spring-multirabbit-extended-example-java - 2.3.1-SNAPSHOT + 2.4.0-SNAPSHOT jar Spring MultiRabbit Extension Example diff --git a/spring-multirabbit-integration/pom.xml b/spring-multirabbit-integration/pom.xml index a76994c..c2f5b07 100644 --- a/spring-multirabbit-integration/pom.xml +++ b/spring-multirabbit-integration/pom.xml @@ -12,7 +12,7 @@ com.free-now.multirabbit spring-multirabbit-integration - 2.3.1-SNAPSHOT + 2.4.0-SNAPSHOT jar Spring MultiRabbit Library Integration Tests diff --git a/spring-multirabbit/pom.xml b/spring-multirabbit/pom.xml index 6a23d31..fa1288c 100644 --- a/spring-multirabbit/pom.xml +++ b/spring-multirabbit/pom.xml @@ -6,7 +6,7 @@ com.free-now.multirabbit spring-multirabbit-parent - 2.3.1-SNAPSHOT + 2.4.0-SNAPSHOT ../ From 74c8b97b0c9a8c4538ce9c3b433ade16c420d5b6 Mon Sep 17 00:00:00 2001 From: Wander Costa Date: Sat, 6 Mar 2021 23:03:15 +0100 Subject: [PATCH 2/4] GH-35 Updated for compatibility with SpringBoot 2.4 --- oss-checkstyle.xml | 2 +- pom.xml | 3 +- .../spring-multirabbit-example-java/pom.xml | 2 +- .../src/main/resources/application.yml | 1 + .../spring-multirabbit-example-kotlin/pom.xml | 2 +- .../src/main/resources/application.yml | 1 + .../pom.xml | 2 +- spring-multirabbit-integration/pom.xml | 78 ----- .../amqp/BeansValidationTest.java | 102 ------ .../amqp/ExternalConfigurationTest.java | 96 ------ .../src/test/resources/application.yml | 12 - spring-multirabbit/pom.xml | 5 + .../MultiRabbitBootstrapConfiguration.java | 28 +- ...itListenerAnnotationBeanPostProcessor.java | 44 +-- .../amqp/MultiRabbitAutoConfiguration.java | 51 +-- .../amqp/MultiRabbitConstants.java | 8 +- .../AutoConfigInitializationTest.java | 32 +- ...MultiRabbitBootstrapConfigurationTest.java | 31 +- .../amqp/ExternalConfigurationTest.java | 146 +++++++++ .../MultiRabbitAutoConfigurationTest.java | 293 +++++++++++++++++- ...ltiRabbitConnectionFactoryCreatorTest.java | 75 +++-- .../application-three-brokers.properties | 4 - .../src/test/resources/application.yml | 11 - 23 files changed, 600 insertions(+), 429 deletions(-) delete mode 100644 spring-multirabbit-integration/pom.xml delete mode 100644 spring-multirabbit-integration/src/test/java/springframework/boot/autoconfigure/amqp/BeansValidationTest.java delete mode 100644 spring-multirabbit-integration/src/test/java/springframework/boot/autoconfigure/amqp/ExternalConfigurationTest.java delete mode 100644 spring-multirabbit-integration/src/test/resources/application.yml create mode 100644 spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/ExternalConfigurationTest.java delete mode 100644 spring-multirabbit/src/test/resources/application-three-brokers.properties delete mode 100644 spring-multirabbit/src/test/resources/application.yml diff --git a/oss-checkstyle.xml b/oss-checkstyle.xml index cc016a0..f17e8ac 100644 --- a/oss-checkstyle.xml +++ b/oss-checkstyle.xml @@ -160,7 +160,7 @@ - + diff --git a/pom.xml b/pom.xml index 18761c8..f46749e 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,6 @@ spring-multirabbit - spring-multirabbit-integration spring-multirabbit-examples/spring-multirabbit-example-java spring-multirabbit-examples/spring-multirabbit-example-kotlin spring-multirabbit-examples/spring-multirabbit-extension-example @@ -38,7 +37,7 @@ UTF-8 1.8 1.8 - 2.3.9.RELEASE + 2.4.0 diff --git a/spring-multirabbit-examples/spring-multirabbit-example-java/pom.xml b/spring-multirabbit-examples/spring-multirabbit-example-java/pom.xml index 904b7e0..a6ab308 100644 --- a/spring-multirabbit-examples/spring-multirabbit-example-java/pom.xml +++ b/spring-multirabbit-examples/spring-multirabbit-example-java/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 2.3.9.RELEASE + 2.4.0 diff --git a/spring-multirabbit-examples/spring-multirabbit-example-java/src/main/resources/application.yml b/spring-multirabbit-examples/spring-multirabbit-example-java/src/main/resources/application.yml index 241ea4a..489b41a 100644 --- a/spring-multirabbit-examples/spring-multirabbit-example-java/src/main/resources/application.yml +++ b/spring-multirabbit-examples/spring-multirabbit-example-java/src/main/resources/application.yml @@ -3,6 +3,7 @@ spring: host: localhost port: 5672 multirabbitmq: + enabled: true connections: connectionNameA: host: localhost diff --git a/spring-multirabbit-examples/spring-multirabbit-example-kotlin/pom.xml b/spring-multirabbit-examples/spring-multirabbit-example-kotlin/pom.xml index b7aa2c7..4a1fff7 100644 --- a/spring-multirabbit-examples/spring-multirabbit-example-kotlin/pom.xml +++ b/spring-multirabbit-examples/spring-multirabbit-example-kotlin/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 2.3.9.RELEASE + 2.4.0 diff --git a/spring-multirabbit-examples/spring-multirabbit-example-kotlin/src/main/resources/application.yml b/spring-multirabbit-examples/spring-multirabbit-example-kotlin/src/main/resources/application.yml index 241ea4a..489b41a 100644 --- a/spring-multirabbit-examples/spring-multirabbit-example-kotlin/src/main/resources/application.yml +++ b/spring-multirabbit-examples/spring-multirabbit-example-kotlin/src/main/resources/application.yml @@ -3,6 +3,7 @@ spring: host: localhost port: 5672 multirabbitmq: + enabled: true connections: connectionNameA: host: localhost diff --git a/spring-multirabbit-examples/spring-multirabbit-extension-example/pom.xml b/spring-multirabbit-examples/spring-multirabbit-extension-example/pom.xml index eabe13a..bde3ed8 100644 --- a/spring-multirabbit-examples/spring-multirabbit-extension-example/pom.xml +++ b/spring-multirabbit-examples/spring-multirabbit-extension-example/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 2.3.9.RELEASE + 2.4.0 diff --git a/spring-multirabbit-integration/pom.xml b/spring-multirabbit-integration/pom.xml deleted file mode 100644 index c2f5b07..0000000 --- a/spring-multirabbit-integration/pom.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - 4.0.0 - - - org.springframework.boot - spring-boot-starter-parent - 2.3.9.RELEASE - - - - com.free-now.multirabbit - spring-multirabbit-integration - 2.4.0-SNAPSHOT - jar - - Spring MultiRabbit Library Integration Tests - Module for integration tests with a running SpringBoot instance - - - 1.8 - 1.8 - - - - - com.free-now.multirabbit - spring-multirabbit - ${project.version} - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-amqp - - - org.springframework.boot - spring-boot-starter-test - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.mockito - mockito-junit-jupiter - test - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.2 - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - - \ No newline at end of file diff --git a/spring-multirabbit-integration/src/test/java/springframework/boot/autoconfigure/amqp/BeansValidationTest.java b/spring-multirabbit-integration/src/test/java/springframework/boot/autoconfigure/amqp/BeansValidationTest.java deleted file mode 100644 index ef541d2..0000000 --- a/spring-multirabbit-integration/src/test/java/springframework/boot/autoconfigure/amqp/BeansValidationTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package springframework.boot.autoconfigure.amqp; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.amqp.rabbit.annotation.EnableRabbit; -import org.springframework.amqp.rabbit.annotation.Exchange; -import org.springframework.amqp.rabbit.annotation.MultiRabbitListenerAnnotationBeanPostProcessor; -import org.springframework.amqp.rabbit.annotation.Queue; -import org.springframework.amqp.rabbit.annotation.QueueBinding; -import org.springframework.amqp.rabbit.annotation.RabbitListener; -import org.springframework.amqp.rabbit.annotation.RabbitListenerAnnotationBeanPostProcessor; -import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; -import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.amqp.rabbit.connection.SimpleRoutingConnectionFactory; -import org.springframework.amqp.rabbit.core.RabbitAdmin; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.amqp.ConnectionFactoryContextWrapper; -import org.springframework.boot.autoconfigure.amqp.MultiRabbitAutoConfiguration; -import org.springframework.boot.autoconfigure.amqp.MultiRabbitConstants; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.ApplicationContext; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.util.Arrays; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@EnableRabbit -@ExtendWith(SpringExtension.class) -@SuppressWarnings("EmptyMethod") -@SpringBootTest(classes = MultiRabbitAutoConfiguration.class) -class BeansValidationTest { - - private static final String CONNECTION_A = "connectionNameA"; - private static final String CONNECTION_B = "connectionNameB"; - - @Autowired - private ApplicationContext applicationContext; - - @Autowired - private ConnectionFactory connectionFactory; - - @Autowired - private RabbitListenerAnnotationBeanPostProcessor rabbitListenerAnnotationBeanPostProcessor; - - @Test - void shouldResolveSimpleRoutingConnectionFactoryBean() { - assertTrue(connectionFactory instanceof SimpleRoutingConnectionFactory); - } - - @Test - void shouldResolveContainerFactoryBeans() { - List beans = Arrays.asList(applicationContext - .getBeanNamesForType(SimpleRabbitListenerContainerFactory.class)); - assertTrue(beans.containsAll(Arrays.asList("rabbitListenerContainerFactory", CONNECTION_A, CONNECTION_B))); - beans.forEach(bean -> assertNotNull(applicationContext - .getBean(bean, SimpleRabbitListenerContainerFactory.class))); - } - - @Test - void shouldResolveConnectionFactoryContextWrapper() { - assertNotNull(applicationContext.getBean(ConnectionFactoryContextWrapper.class)); - } - - @Test - void shouldResolveRabbitAdminBeans() { - List beans = Arrays.asList(applicationContext.getBeanNamesForType(RabbitAdmin.class)); - assertTrue(beans.containsAll(Arrays.asList( - MultiRabbitConstants.DEFAULT_RABBIT_ADMIN_BEAN_NAME, - CONNECTION_A + MultiRabbitConstants.RABBIT_ADMIN_SUFFIX, - CONNECTION_B + MultiRabbitConstants.RABBIT_ADMIN_SUFFIX))); - beans.forEach(bean -> assertNotNull(applicationContext.getBean(bean, RabbitAdmin.class))); - } - - @Test - void shouldResolveExtendedRabbitListenerAnnotationBeanPostProcessor() { - assertTrue(rabbitListenerAnnotationBeanPostProcessor instanceof MultiRabbitListenerAnnotationBeanPostProcessor); - } - - @RabbitListener(bindings = @QueueBinding( - value = @Queue("queue"), - exchange = @Exchange("exchange"), - key = "key")) - void listen() { - } - - @RabbitListener(containerFactory = CONNECTION_A, bindings = @QueueBinding( - value = @Queue("queue"), - exchange = @Exchange("exchange"), - key = "key")) - void listenConnectionNameA() { - } - - @RabbitListener(containerFactory = CONNECTION_B, bindings = @QueueBinding( - value = @Queue("queue"), - exchange = @Exchange("exchange"), - key = "key")) - void listenConnectionNameB() { - } -} diff --git a/spring-multirabbit-integration/src/test/java/springframework/boot/autoconfigure/amqp/ExternalConfigurationTest.java b/spring-multirabbit-integration/src/test/java/springframework/boot/autoconfigure/amqp/ExternalConfigurationTest.java deleted file mode 100644 index 90fd198..0000000 --- a/spring-multirabbit-integration/src/test/java/springframework/boot/autoconfigure/amqp/ExternalConfigurationTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package springframework.boot.autoconfigure.amqp; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.amqp.rabbit.annotation.EnableRabbit; -import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; -import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.amqp.rabbit.connection.SimpleResourceHolder; -import org.springframework.amqp.rabbit.core.RabbitAdmin; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.amqp.MultiRabbitAutoConfiguration; -import org.springframework.boot.autoconfigure.amqp.MultiRabbitConnectionFactoryWrapper; -import org.springframework.boot.autoconfigure.amqp.MultiRabbitProperties; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -@EnableRabbit -@ExtendWith(SpringExtension.class) -@SpringBootTest(classes = ExternalConfigurationTest.Config.class) -class ExternalConfigurationTest { - - private static final String CONNECTION_KEY = "externalConnectionKey"; - private static final ConnectionFactory CONNECTION_FACTORY = mock(ConnectionFactory.class); - private static final SimpleRabbitListenerContainerFactory CONTAINER_FACTORY - = mock(SimpleRabbitListenerContainerFactory.class); - private static final RabbitAdmin RABBIT_ADMIN = mock(RabbitAdmin.class); - private static final ConnectionFactory DEFAULT_CONNECTION_FACTORY = mock(ConnectionFactory.class); - - @Autowired - private ConnectionFactory connectionFactory; - - @Autowired - private MultiRabbitProperties multiRabbitProperties; - - @AfterEach - void after() { - // For the sake of simplicity, the mocks are static so as to be shared among classes. - // Thus, they need to reset after using, to avoid interferences on the next test. - reset(CONNECTION_FACTORY, CONTAINER_FACTORY, RABBIT_ADMIN); - } - - @Test - void shouldResolveDefaultExternalConnectionFactory() { - connectionFactory.getVirtualHost(); - verify(DEFAULT_CONNECTION_FACTORY).getVirtualHost(); - } - - @Test - void shouldResolveExternalConnectionFactory() { - SimpleResourceHolder.bind(connectionFactory, CONNECTION_KEY); - connectionFactory.getVirtualHost(); - SimpleResourceHolder.unbind(connectionFactory); - verify(CONNECTION_FACTORY).getVirtualHost(); - } - - @Test - void shouldResolveExistentConnectionFactoriesFromMulti() { - multiRabbitProperties.getConnections().keySet().forEach(key -> { - SimpleResourceHolder.bind(connectionFactory, key); - connectionFactory.getVirtualHost(); - SimpleResourceHolder.unbind(connectionFactory); - }); - assertMocksNotTouched(); - } - - private void assertMocksNotTouched() { - verify(CONNECTION_FACTORY, never()).getVirtualHost(); - verifyNoMoreInteractions(CONTAINER_FACTORY); - verifyNoMoreInteractions(RABBIT_ADMIN); - } - - @Configuration - @Import(MultiRabbitAutoConfiguration.class) - static class Config { - - @Bean - @ConditionalOnClass(MultiRabbitConnectionFactoryWrapper.class) - static MultiRabbitConnectionFactoryWrapper externalWrapper() { - MultiRabbitConnectionFactoryWrapper wrapper = new MultiRabbitConnectionFactoryWrapper(); - wrapper.addConnectionFactory(CONNECTION_KEY, CONNECTION_FACTORY, CONTAINER_FACTORY, RABBIT_ADMIN); - wrapper.setDefaultConnectionFactory(DEFAULT_CONNECTION_FACTORY); - return wrapper; - } - } -} diff --git a/spring-multirabbit-integration/src/test/resources/application.yml b/spring-multirabbit-integration/src/test/resources/application.yml deleted file mode 100644 index 241ea4a..0000000 --- a/spring-multirabbit-integration/src/test/resources/application.yml +++ /dev/null @@ -1,12 +0,0 @@ -spring: - rabbitmq: - host: localhost - port: 5672 - multirabbitmq: - connections: - connectionNameA: - host: localhost - port: 5673 - connectionNameB: - host: localhost - port: 5674 \ No newline at end of file diff --git a/spring-multirabbit/pom.xml b/spring-multirabbit/pom.xml index fa1288c..1ca0c3f 100644 --- a/spring-multirabbit/pom.xml +++ b/spring-multirabbit/pom.xml @@ -30,6 +30,11 @@ javax.validation validation-api + + org.springframework.boot + spring-boot-starter-test + test + org.junit.jupiter junit-jupiter-engine diff --git a/spring-multirabbit/src/main/java/org/springframework/amqp/rabbit/annotation/MultiRabbitBootstrapConfiguration.java b/spring-multirabbit/src/main/java/org/springframework/amqp/rabbit/annotation/MultiRabbitBootstrapConfiguration.java index 1555c14..7f0bb9d 100644 --- a/spring-multirabbit/src/main/java/org/springframework/amqp/rabbit/annotation/MultiRabbitBootstrapConfiguration.java +++ b/spring-multirabbit/src/main/java/org/springframework/amqp/rabbit/annotation/MultiRabbitBootstrapConfiguration.java @@ -1,9 +1,13 @@ package org.springframework.amqp.rabbit.annotation; -import org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils; +import static org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME; +import static org.springframework.boot.autoconfigure.amqp.MultiRabbitConstants.MULTI_RABBIT_ENABLED_PROPERTY; + import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotationMetadata; /** @@ -18,13 +22,29 @@ * @see org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry * @see EnableRabbit */ -public class MultiRabbitBootstrapConfiguration implements ImportBeanDefinitionRegistrar { +public class MultiRabbitBootstrapConfiguration implements ImportBeanDefinitionRegistrar, EnvironmentAware { + + private Environment environment; @Override public void registerBeanDefinitions(final AnnotationMetadata importingClassMetadata, final BeanDefinitionRegistry registry) { - registry.registerBeanDefinition(RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME, - new RootBeanDefinition(MultiRabbitListenerAnnotationBeanPostProcessor.class)); + if (isMultiRabbitEnabled()) { + if (registry.containsBeanDefinition(RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)) { + registry.removeBeanDefinition(RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME); + } + registry.registerBeanDefinition(RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME, + new RootBeanDefinition(MultiRabbitListenerAnnotationBeanPostProcessor.class)); + } + } + + private boolean isMultiRabbitEnabled() { + final String isMultiEnabledStr = this.environment.getProperty(MULTI_RABBIT_ENABLED_PROPERTY); + return Boolean.parseBoolean(isMultiEnabledStr); } + @Override + public void setEnvironment(final Environment environment) { + this.environment = environment; + } } diff --git a/spring-multirabbit/src/main/java/org/springframework/amqp/rabbit/annotation/MultiRabbitListenerAnnotationBeanPostProcessor.java b/spring-multirabbit/src/main/java/org/springframework/amqp/rabbit/annotation/MultiRabbitListenerAnnotationBeanPostProcessor.java index 606dfd6..927e1a7 100644 --- a/spring-multirabbit/src/main/java/org/springframework/amqp/rabbit/annotation/MultiRabbitListenerAnnotationBeanPostProcessor.java +++ b/spring-multirabbit/src/main/java/org/springframework/amqp/rabbit/annotation/MultiRabbitListenerAnnotationBeanPostProcessor.java @@ -4,40 +4,34 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import org.springframework.amqp.core.AbstractDeclarable; +import java.util.Collection; import org.springframework.amqp.core.Declarable; -import org.springframework.amqp.rabbit.core.RabbitAdmin; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; import org.springframework.util.StringUtils; /** * An extension of {@link RabbitListenerAnnotationBeanPostProcessor} that attaches the processing of beans for * Exchanges, Queues, and Bindings after they are created. *

- * This processing enables each {@link RabbitAdmin} to differentiate which beans corresponds to that specific - * {@link RabbitAdmin}, preventing the server from being populated with non-expected structures from other servers. + * This processing enables each {@link org.springframework.amqp.rabbit.core.RabbitAdmin} to differentiate which beans + * corresponds to that specific {@link org.springframework.amqp.rabbit.core.RabbitAdmin}, preventing the server from + * being populated with non-expected structures from other servers. * * @author Wander Costa * @see RabbitListenerAnnotationBeanPostProcessor */ public final class MultiRabbitListenerAnnotationBeanPostProcessor - extends RabbitListenerAnnotationBeanPostProcessor - implements ApplicationContextAware { - - private ApplicationContext applicationContext; + extends RabbitListenerAnnotationBeanPostProcessor { @Override - protected void processAmqpListener(final RabbitListener rabbitListener, - final Method method, - final Object bean, - final String beanName) { + protected Collection processAmqpListener(final RabbitListener rabbitListener, + final Method method, + final Object bean, + final String beanName) { final String rabbitAdmin = RabbitAdminNameResolver.resolve(rabbitListener); final RabbitListener rabbitListenerRef = proxyIfAdminNotPresent(rabbitListener, rabbitAdmin); - super.processAmqpListener(rabbitListenerRef, method, bean, beanName); - applicationContext.getBeansOfType(AbstractDeclarable.class).values().stream() - .filter(this::isNotProcessed) - .forEach(exchange -> exchange.setAdminsThatShouldDeclare(rabbitAdmin)); + final Collection declarables = super.processAmqpListener(rabbitListenerRef, method, bean, beanName); + declarables.forEach(declarable -> declarable.setAdminsThatShouldDeclare(rabbitAdmin)); + return declarables; } private RabbitListener proxyIfAdminNotPresent(final RabbitListener rabbitListener, final String rabbitAdmin) { @@ -49,20 +43,6 @@ private RabbitListener proxyIfAdminNotPresent(final RabbitListener rabbitListene new RabbitListenerAdminReplacementInvocationHandler(rabbitListener, rabbitAdmin)); } - /** - * Verifies the presence of an instance of RabbitAdmin or this object, as fallback. - */ - private boolean isNotProcessed(final Declarable declarable) { - return declarable.getDeclaringAdmins() == null - || (declarable.getDeclaringAdmins().stream().noneMatch(item -> item == this) - && declarable.getDeclaringAdmins().stream().noneMatch(item -> item instanceof RabbitAdmin)); - } - - @Override - public void setApplicationContext(final ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - /** * An {@link InvocationHandler} to provide a replacing admin() parameter of the listener. */ diff --git a/spring-multirabbit/src/main/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfiguration.java b/spring-multirabbit/src/main/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfiguration.java index 33801f5..694f530 100644 --- a/spring-multirabbit/src/main/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfiguration.java +++ b/spring-multirabbit/src/main/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfiguration.java @@ -1,6 +1,10 @@ package org.springframework.boot.autoconfigure.amqp; import com.rabbitmq.client.Channel; +import com.rabbitmq.client.impl.CredentialsProvider; +import com.rabbitmq.client.impl.CredentialsRefreshService; +import java.util.Collections; +import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.AmqpAdmin; @@ -17,6 +21,7 @@ import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration.RabbitConnectionFactoryCreator; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -29,11 +34,9 @@ import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; +import org.springframework.core.io.ResourceLoader; import org.springframework.util.StringUtils; -import java.util.Collections; -import java.util.Map; - /** * Class responsible for auto-configuring the necessary beans to enable multiple RabbitMQ servers. * @@ -62,14 +65,14 @@ public AmqpAdmin amqpAdmin(final ConnectionFactory connectionFactory) { } /** - * Returns a {@link RabbitAutoConfiguration.RabbitConnectionFactoryCreator}. + * Returns a {@link RabbitConnectionFactoryCreator}. * - * @return a {@link RabbitAutoConfiguration.RabbitConnectionFactoryCreator}. + * @return a {@link RabbitConnectionFactoryCreator}. */ @Primary @Bean(MultiRabbitConstants.CONNECTION_FACTORY_CREATOR_BEAN_NAME) - public RabbitAutoConfiguration.RabbitConnectionFactoryCreator rabbitConnectionFactoryCreator() { - return new RabbitAutoConfiguration.RabbitConnectionFactoryCreator(); + public RabbitConnectionFactoryCreator rabbitConnectionFactoryCreator() { + return new RabbitConnectionFactoryCreator(); } /** @@ -77,24 +80,20 @@ public RabbitAutoConfiguration.RabbitConnectionFactoryCreator rabbitConnectionFa */ @Configuration @DependsOn(MultiRabbitConstants.CONNECTION_FACTORY_CREATOR_BEAN_NAME) + @ConditionalOnProperty(prefix = "spring.multirabbitmq", name = "enabled", havingValue = "true") protected static class MultiRabbitConnectionFactoryCreator implements BeanFactoryAware, ApplicationContextAware { private ConfigurableListableBeanFactory beanFactory; private ApplicationContext applicationContext; - private final RabbitAutoConfiguration.RabbitConnectionFactoryCreator springFactoryCreator; - private final ObjectProvider connectionNameStrategy; + private final RabbitConnectionFactoryCreator springFactoryCreator; /** * Creates a new MultiRabbitConnectionFactoryCreator for instantiation of beans. * - * @param springFactoryCreator The RabbitConnectionFactoryCreator. - * @param connectionNameStrategy The connection name strategy. + * @param springFactoryCreator The RabbitConnectionFactoryCreator. */ - MultiRabbitConnectionFactoryCreator( - final RabbitAutoConfiguration.RabbitConnectionFactoryCreator springFactoryCreator, - final ObjectProvider connectionNameStrategy) { + MultiRabbitConnectionFactoryCreator(final RabbitConnectionFactoryCreator springFactoryCreator) { this.springFactoryCreator = springFactoryCreator; - this.connectionNameStrategy = connectionNameStrategy; } /** @@ -104,6 +103,7 @@ protected static class MultiRabbitConnectionFactoryCreator implements BeanFactor * @return The wrapper. */ @Bean + @ConditionalOnMissingBean public ConnectionFactoryContextWrapper contextWrapper(final ConnectionFactory connectionFactory) { return new ConnectionFactoryContextWrapper(connectionFactory); } @@ -133,9 +133,14 @@ public MultiRabbitConnectionFactoryWrapper externalEmptyWrapper() { public ConnectionFactory routingConnectionFactory( final RabbitProperties rabbitProperties, final MultiRabbitProperties multiRabbitProperties, - final MultiRabbitConnectionFactoryWrapper externalWrapper) throws Exception { + final MultiRabbitConnectionFactoryWrapper externalWrapper, + final ResourceLoader resourceLoader, + final ObjectProvider credentialsProvider, + final ObjectProvider credentialsRefreshService, + final ObjectProvider connectionNameStrategy) throws Exception { final MultiRabbitConnectionFactoryWrapper internalWrapper - = instantiateConnectionFactories(rabbitProperties, multiRabbitProperties); + = instantiateConnectionFactories(rabbitProperties, multiRabbitProperties, resourceLoader, + credentialsProvider, credentialsRefreshService, connectionNameStrategy); final MultiRabbitConnectionFactoryWrapper aggregatedWrapper = aggregateConnectionFactoryWrappers(internalWrapper, externalWrapper); @@ -188,7 +193,11 @@ private void copyConnectionSets( */ private MultiRabbitConnectionFactoryWrapper instantiateConnectionFactories( final RabbitProperties rabbitProperties, - final MultiRabbitProperties multiRabbitProperties) throws Exception { + final MultiRabbitProperties multiRabbitProperties, + final ResourceLoader resourceLoader, + final ObjectProvider credentialsProvider, + final ObjectProvider credentialsRefreshService, + final ObjectProvider connectionNameStrategy) throws Exception { final MultiRabbitConnectionFactoryWrapper wrapper = new MultiRabbitConnectionFactoryWrapper(); final Map propertiesMap = multiRabbitProperties != null @@ -197,7 +206,8 @@ private MultiRabbitConnectionFactoryWrapper instantiateConnectionFactories( for (Map.Entry entry : propertiesMap.entrySet()) { final CachingConnectionFactory connectionFactory - = springFactoryCreator.rabbitConnectionFactory(entry.getValue(), connectionNameStrategy); + = springFactoryCreator.rabbitConnectionFactory(entry.getValue(), resourceLoader, + credentialsProvider, credentialsRefreshService, connectionNameStrategy); final SimpleRabbitListenerContainerFactory containerFactory = newContainerFactory(connectionFactory); final RabbitAdmin rabbitAdmin = newRabbitAdmin(connectionFactory); wrapper.addConnectionFactory(entry.getKey(), connectionFactory, containerFactory, rabbitAdmin); @@ -217,7 +227,8 @@ private MultiRabbitConnectionFactoryWrapper instantiateConnectionFactories( final ConnectionFactory defaultConnectionFactory = StringUtils.hasText(defaultConnectionFactoryKey) ? wrapper.getConnectionFactories().get(defaultConnectionFactoryKey) - : springFactoryCreator.rabbitConnectionFactory(rabbitProperties, connectionNameStrategy); + : springFactoryCreator.rabbitConnectionFactory(rabbitProperties, resourceLoader, + credentialsProvider, credentialsRefreshService, connectionNameStrategy); wrapper.setDefaultConnectionFactory(defaultConnectionFactory); return wrapper; diff --git a/spring-multirabbit/src/main/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitConstants.java b/spring-multirabbit/src/main/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitConstants.java index c344574..013dbe6 100644 --- a/spring-multirabbit/src/main/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitConstants.java +++ b/spring-multirabbit/src/main/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitConstants.java @@ -5,10 +5,12 @@ */ public final class MultiRabbitConstants { - static final String CONNECTION_FACTORY_BEAN_NAME = "multiRabbitConnectionFactory"; - static final String CONNECTION_FACTORY_CREATOR_BEAN_NAME = "rabbitConnectionFactoryCreator"; - public static final String DEFAULT_RABBIT_ADMIN_BEAN_NAME = "defaultRabbitAdmin"; + public static final String CONNECTION_FACTORY_BEAN_NAME = "multiRabbitConnectionFactory"; + public static final String CONNECTION_FACTORY_CREATOR_BEAN_NAME = "rabbitConnectionFactoryCreator"; + public static final String DEFAULT_RABBIT_ADMIN_BEAN_NAME = "amqpAdmin"; public static final String RABBIT_ADMIN_SUFFIX = "-admin"; + public static final String DEFAULT_CONTAINER_FACTORY_BEAN_NAME = "rabbitListenerContainerFactory"; + public static final String MULTI_RABBIT_ENABLED_PROPERTY = "spring.multirabbitmq.enabled"; private MultiRabbitConstants() { } diff --git a/spring-multirabbit/src/test/java/org/springframework/amqp/rabbit/annotation/AutoConfigInitializationTest.java b/spring-multirabbit/src/test/java/org/springframework/amqp/rabbit/annotation/AutoConfigInitializationTest.java index 0e8d5b0..e740e8b 100644 --- a/spring-multirabbit/src/test/java/org/springframework/amqp/rabbit/annotation/AutoConfigInitializationTest.java +++ b/spring-multirabbit/src/test/java/org/springframework/amqp/rabbit/annotation/AutoConfigInitializationTest.java @@ -1,16 +1,14 @@ package org.springframework.amqp.rabbit.annotation; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.amqp.rabbit.core.RabbitAdmin; +import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.amqp.MultiRabbitAutoConfiguration; import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.PropertySource; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.stereotype.Component; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * {@link MultiRabbitAutoConfiguration} is normally triggered before the processing of the Listeners by the @@ -19,19 +17,19 @@ * This test makes sure to test MultiRabbit without the injection of a RabbitTemplate as a workaround for the * initialization. */ -// TODO https://github.com/freenowtech/spring-multirabbit/issues/49 -@Disabled class AutoConfigInitializationTest { - private static final int ADMINS = 3; + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(MultiRabbitAutoConfiguration.class, RabbitAutoConfiguration.class)); @Test void shouldStartContextWithoutConnectionFactory() { - final AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext( - ThreeBrokersConfig.class, MultiRabbitAutoConfiguration.class, RabbitAutoConfiguration.class, - ListenerBeans.class); - assertEquals(ADMINS, ctx.getBeansOfType(RabbitAdmin.class).size()); - ctx.close(); // Close and stop the listeners + this.contextRunner + .withPropertyValues("spring.multirabbitmq.enabled=true") + .withPropertyValues("spring.multirabbitmq.connections.broker1.port=5673") + .withPropertyValues("spring.multirabbitmq.connections.broker2.port=5674") + .withBean(ListenerBeans.class) + .run((context) -> assertThat(context.getBeansOfType(RabbitAdmin.class)).hasSize(3)); } @Component @@ -59,12 +57,4 @@ void listenBroker1(final String message) { void listenBroker2(final String message) { } } - - /** - * Configuration to provide 3 brokers. - */ - @Configuration - @PropertySource("classpath:application-three-brokers.properties") - public static class ThreeBrokersConfig { - } } diff --git a/spring-multirabbit/src/test/java/org/springframework/amqp/rabbit/annotation/MultiRabbitBootstrapConfigurationTest.java b/spring-multirabbit/src/test/java/org/springframework/amqp/rabbit/annotation/MultiRabbitBootstrapConfigurationTest.java index d172748..694cbf3 100644 --- a/spring-multirabbit/src/test/java/org/springframework/amqp/rabbit/annotation/MultiRabbitBootstrapConfigurationTest.java +++ b/spring-multirabbit/src/test/java/org/springframework/amqp/rabbit/annotation/MultiRabbitBootstrapConfigurationTest.java @@ -1,5 +1,11 @@ package org.springframework.amqp.rabbit.annotation; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -7,8 +13,8 @@ import org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; - -import static org.mockito.Mockito.verify; +import org.springframework.boot.autoconfigure.amqp.MultiRabbitConstants; +import org.springframework.core.env.Environment; @ExtendWith(MockitoExtension.class) class MultiRabbitBootstrapConfigurationTest { @@ -18,12 +24,33 @@ class MultiRabbitBootstrapConfigurationTest { @Mock private BeanDefinitionRegistry registry; + @Mock + private Environment environment; + + @BeforeEach + void beforeEach() { + configuration.setEnvironment(environment); + } + @Test + @DisplayName("should process MultiRabbitBootstrapConfiguration if enabled") void shouldCreateMultiRabbitListenerAnnotationBeanPostProcessorBean() { + when(environment.getProperty(MultiRabbitConstants.MULTI_RABBIT_ENABLED_PROPERTY)).thenReturn("true"); + configuration.registerBeanDefinitions(null, registry); verify(registry).registerBeanDefinition( RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME, new RootBeanDefinition(MultiRabbitListenerAnnotationBeanPostProcessor.class)); } + + @Test + @DisplayName("should not process MultiRabbitBootstrapConfiguration if disabled") + void shouldNotCreateMultiRabbitListenerAnnotationBeanPostProcessorBean() { + configuration.registerBeanDefinitions(null, registry); + + verify(registry, never()).registerBeanDefinition( + RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME, + new RootBeanDefinition(MultiRabbitListenerAnnotationBeanPostProcessor.class)); + } } diff --git a/spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/ExternalConfigurationTest.java b/spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/ExternalConfigurationTest.java new file mode 100644 index 0000000..57c548c --- /dev/null +++ b/spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/ExternalConfigurationTest.java @@ -0,0 +1,146 @@ +package org.springframework.boot.autoconfigure.amqp; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; + +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.connection.RoutingConnectionFactory; +import org.springframework.amqp.rabbit.connection.SimpleRoutingConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitAdmin; +import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +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; + +class ExternalConfigurationTest { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(MultiRabbitAutoConfiguration.class, RabbitAutoConfiguration.class, + ExternalConfig.class)); + + @Test + @DisplayName("should not find beans from external integration when disabled") + void shouldEnsureMultiRabbitNotLoadedWhenDisabled() { + final String broker1 = "broker1"; + final String broker2 = "broker2"; + this.contextRunner + .withPropertyValues("spring.multirabbitmq.enabled=false") + .withPropertyValues("spring.multirabbitmq.connections." + broker1 + ".port=5673") + .withPropertyValues("spring.multirabbitmq.connections." + broker2 + ".port=5674") + .run((context) -> { + final RabbitAdmin defaultRabbitAdmin = context.getBean( + MultiRabbitConstants.DEFAULT_RABBIT_ADMIN_BEAN_NAME, RabbitAdmin.class); + final ThrowingCallable broker1Admin = () -> context.getBean( + broker1.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX), RabbitAdmin.class); + final ThrowingCallable broker2Admin = () -> context.getBean( + broker2.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX), RabbitAdmin.class); + final ThrowingCallable externalAdmin = () -> context.getBean( + broker2.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX), RabbitAdmin.class); + assertThat(defaultRabbitAdmin).isNotNull(); + assertThatThrownBy(broker1Admin).isInstanceOf(NoSuchBeanDefinitionException.class); + assertThatThrownBy(broker2Admin).isInstanceOf(NoSuchBeanDefinitionException.class); + assertThatThrownBy(externalAdmin).isInstanceOf(NoSuchBeanDefinitionException.class); + + final RabbitListenerContainerFactory defaultContainerFactory = context.getBean( + MultiRabbitConstants.DEFAULT_CONTAINER_FACTORY_BEAN_NAME, + RabbitListenerContainerFactory.class); + final ThrowingCallable broker1ContainerFactory = () -> context.getBean(broker1, + RabbitListenerContainerFactory.class); + final ThrowingCallable broker2ContainerFactory = () -> context.getBean(broker2, + RabbitListenerContainerFactory.class); + assertThat(defaultContainerFactory).isNotNull(); + final ThrowingCallable externalContainerFactory = () -> context.getBean(broker2, + RabbitListenerContainerFactory.class); + assertThatThrownBy(broker1ContainerFactory).isInstanceOf(NoSuchBeanDefinitionException.class); + assertThatThrownBy(broker2ContainerFactory).isInstanceOf(NoSuchBeanDefinitionException.class); + assertThatThrownBy(externalContainerFactory).isInstanceOf(NoSuchBeanDefinitionException.class); + + final ConnectionFactory connectionFactory = context.getBean(ConnectionFactory.class); + assertThat(connectionFactory).isNotInstanceOf(RoutingConnectionFactory.class); + }); + } + + @Test + @DisplayName("should ensure beans from external integration") + void shouldEnsureBeansFromExternalIntegration() { + final String broker1 = "broker1"; + final String broker2 = "broker2"; + this.contextRunner + .withPropertyValues("spring.multirabbitmq.enabled=true") + .withPropertyValues("spring.multirabbitmq.connections." + broker1 + ".port=5673") + .withPropertyValues("spring.multirabbitmq.connections." + broker2 + ".port=5674") + .run((context) -> { + final RabbitAdmin defaultRabbitAdmin = context.getBean( + MultiRabbitConstants.DEFAULT_RABBIT_ADMIN_BEAN_NAME, RabbitAdmin.class); + final RabbitAdmin broker1Admin = context.getBean( + broker1.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX), RabbitAdmin.class); + final RabbitAdmin broker2Admin = context.getBean( + broker2.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX), RabbitAdmin.class); + final RabbitAdmin externalAdmin = context.getBean( + ExternalConfig.CONNECTION_KEY.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX), + RabbitAdmin.class); + assertThat(defaultRabbitAdmin).isNotNull(); + assertThat(broker1Admin).isNotNull(); + assertThat(broker2Admin).isNotNull(); + assertThat(externalAdmin).isSameAs(ExternalConfig.RABBIT_ADMIN); + + final RabbitListenerContainerFactory defaultContainerFactory = context.getBean( + MultiRabbitConstants.DEFAULT_CONTAINER_FACTORY_BEAN_NAME, + RabbitListenerContainerFactory.class); + final RabbitListenerContainerFactory broker1ContainerFactory = context.getBean(broker1, + RabbitListenerContainerFactory.class); + final RabbitListenerContainerFactory broker2ContainerFactory = context.getBean(broker2, + RabbitListenerContainerFactory.class); + final RabbitListenerContainerFactory externalContainerFactory = context.getBean( + ExternalConfig.CONNECTION_KEY, RabbitListenerContainerFactory.class); + assertThat(defaultContainerFactory).isNotNull(); + assertThat(broker1ContainerFactory).isNotNull(); + assertThat(broker2ContainerFactory).isNotNull(); + assertThat(externalContainerFactory).isSameAs(ExternalConfig.CONTAINER_FACTORY); + + final ConnectionFactory connectionFactory = context.getBean(ConnectionFactory.class); + assertThat(connectionFactory).isInstanceOf(RoutingConnectionFactory.class); + final SimpleRoutingConnectionFactory routingConnectionFactory + = (SimpleRoutingConnectionFactory) connectionFactory; + final ConnectionFactory connectionFactory1 = routingConnectionFactory + .getTargetConnectionFactory(broker1); + final ConnectionFactory connectionFactory2 = routingConnectionFactory + .getTargetConnectionFactory(broker2); + final ConnectionFactory externalConnectionFactory = routingConnectionFactory + .getTargetConnectionFactory(ExternalConfig.CONNECTION_KEY); + assertThat(connectionFactory1).isNotNull(); + assertThat(connectionFactory2).isNotNull(); + assertThat(externalConnectionFactory).isSameAs(ExternalConfig.CONNECTION_FACTORY); + }); + } + + @Configuration + @Import(MultiRabbitAutoConfiguration.class) + static class ExternalConfig { + + private static final String CONNECTION_KEY = "externalConnectionKey"; + private static final ConnectionFactory CONNECTION_FACTORY = mock(ConnectionFactory.class); + private static final SimpleRabbitListenerContainerFactory CONTAINER_FACTORY + = mock(SimpleRabbitListenerContainerFactory.class); + private static final RabbitAdmin RABBIT_ADMIN = mock(RabbitAdmin.class); + private static final ConnectionFactory DEFAULT_CONNECTION_FACTORY = mock(ConnectionFactory.class); + + @Bean + @ConditionalOnClass(MultiRabbitConnectionFactoryWrapper.class) + static MultiRabbitConnectionFactoryWrapper externalWrapper() { + MultiRabbitConnectionFactoryWrapper wrapper = new MultiRabbitConnectionFactoryWrapper(); + wrapper.addConnectionFactory(CONNECTION_KEY, CONNECTION_FACTORY, CONTAINER_FACTORY, RABBIT_ADMIN); + wrapper.setDefaultConnectionFactory(DEFAULT_CONNECTION_FACTORY); + return wrapper; + } + } +} diff --git a/spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfigurationTest.java b/spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfigurationTest.java index d270881..cce5fc0 100644 --- a/spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfigurationTest.java +++ b/spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfigurationTest.java @@ -1,32 +1,297 @@ package org.springframework.boot.autoconfigure.amqp; +import java.util.Collection; +import java.util.Map; +import java.util.stream.Collectors; +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.amqp.core.AbstractExchange; +import org.springframework.amqp.core.Binding; +import org.springframework.amqp.core.Declarable; +import org.springframework.amqp.core.DirectExchange; +import org.springframework.amqp.rabbit.annotation.EnableRabbit; +import org.springframework.amqp.rabbit.annotation.Exchange; +import org.springframework.amqp.rabbit.annotation.MultiRabbitListenerAnnotationBeanPostProcessor; +import org.springframework.amqp.rabbit.annotation.Queue; +import org.springframework.amqp.rabbit.annotation.QueueBinding; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils; import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.connection.RoutingConnectionFactory; +import org.springframework.amqp.rabbit.connection.SimpleRoutingConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitAdmin; +import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.amqp.MultiRabbitAutoConfiguration.MultiRabbitConnectionFactoryCreator; +import org.springframework.boot.test.context.assertj.AssertableApplicationContext; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.stereotype.Component; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -@ExtendWith(MockitoExtension.class) +/** + * Tests for {@link MultiRabbitAutoConfiguration}. + * + * @author Wander Costa + */ class MultiRabbitAutoConfigurationTest { - @Mock - private ConnectionFactory connectionFactory; + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(MultiRabbitAutoConfiguration.class, RabbitAutoConfiguration.class)); - private MultiRabbitAutoConfiguration config() { - return new MultiRabbitAutoConfiguration(); + @Test + @DisplayName("should not find MultiRabbit beans when disabled") + void shouldEnsureMultiRabbitNotLoadedWhenDisabled() { + final String broker1 = ThreeListenersBeans.BROKER_NAME_1; + final String broker2 = ThreeListenersBeans.BROKER_NAME_2; + this.contextRunner + .withPropertyValues("spring.multirabbitmq.enabled=false") + .withPropertyValues("spring.multirabbitmq.connections.broker1.port=5673") + .withPropertyValues("spring.multirabbitmq.connections.broker2.port=5674") + .run((context) -> { + final RabbitAdmin defaultRabbitAdmin = context.getBean( + MultiRabbitConstants.DEFAULT_RABBIT_ADMIN_BEAN_NAME, RabbitAdmin.class); + final ThrowingCallable broker1Admin = () -> context.getBean( + broker1.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX), RabbitAdmin.class); + final ThrowingCallable broker2Admin = () -> context.getBean( + broker2.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX), RabbitAdmin.class); + assertThat(defaultRabbitAdmin).isNotNull(); + assertThatThrownBy(broker1Admin).isInstanceOf(NoSuchBeanDefinitionException.class); + assertThatThrownBy(broker2Admin).isInstanceOf(NoSuchBeanDefinitionException.class); + + final RabbitListenerContainerFactory defaultContainerFactory = context.getBean( + MultiRabbitConstants.DEFAULT_CONTAINER_FACTORY_BEAN_NAME, + RabbitListenerContainerFactory.class); + final ThrowingCallable broker1ContainerFactory = () -> context.getBean(broker1, + RabbitListenerContainerFactory.class); + final ThrowingCallable broker2ContainerFactory = () -> context.getBean(broker2, + RabbitListenerContainerFactory.class); + assertThat(defaultContainerFactory).isNotNull(); + assertThatThrownBy(broker1ContainerFactory).isInstanceOf(NoSuchBeanDefinitionException.class); + assertThatThrownBy(broker2ContainerFactory).isInstanceOf(NoSuchBeanDefinitionException.class); + + final ConnectionFactory connectionFactory = context.getBean(ConnectionFactory.class); + assertThat(connectionFactory).isNotInstanceOf(RoutingConnectionFactory.class); + + final Object annotationBPP = context + .getBean(RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME); + assertThat(annotationBPP).isNotInstanceOf(MultiRabbitListenerAnnotationBeanPostProcessor.class); + + final ThrowingCallable connectionFactoryCreator = () -> context + .getBean(MultiRabbitConnectionFactoryCreator.class); + assertThatThrownBy(connectionFactoryCreator).isInstanceOf(NoSuchBeanDefinitionException.class); + + final ThrowingCallable contextWrapper = () -> context + .getBean(MultiRabbitConnectionFactoryWrapper.class); + assertThatThrownBy(contextWrapper).isInstanceOf(NoSuchBeanDefinitionException.class); + }); + } + + @Test + @DisplayName("should ensure MultiRabbit beans from AutoConfiguration") + void shouldEnsureAutoConfigurationBeans() { + final String broker1 = ThreeListenersBeans.BROKER_NAME_1; + final String broker2 = ThreeListenersBeans.BROKER_NAME_2; + this.contextRunner + .withPropertyValues("spring.multirabbitmq.enabled=true") + .withPropertyValues("spring.multirabbitmq.connections." + broker1 + ".port=5673") + .withPropertyValues("spring.multirabbitmq.connections." + broker2 + ".port=5674") + .run((context) -> { + final RabbitAdmin defaultRabbitAdmin = context.getBean( + MultiRabbitConstants.DEFAULT_RABBIT_ADMIN_BEAN_NAME, RabbitAdmin.class); + final RabbitAdmin broker1Admin = context.getBean( + broker1.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX), RabbitAdmin.class); + final RabbitAdmin broker2Admin = context.getBean( + broker2.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX), RabbitAdmin.class); + assertThat(defaultRabbitAdmin).isNotNull(); + assertThat(broker1Admin).isNotNull(); + assertThat(broker2Admin).isNotNull(); + + final RabbitListenerContainerFactory defaultContainerFactory = context.getBean( + MultiRabbitConstants.DEFAULT_CONTAINER_FACTORY_BEAN_NAME, + RabbitListenerContainerFactory.class); + final RabbitListenerContainerFactory broker1ContainerFactory = context.getBean(broker1, + RabbitListenerContainerFactory.class); + final RabbitListenerContainerFactory broker2ContainerFactory = context.getBean(broker2, + RabbitListenerContainerFactory.class); + assertThat(defaultContainerFactory).isNotNull(); + assertThat(broker1ContainerFactory).isNotNull(); + assertThat(broker2ContainerFactory).isNotNull(); + + final ConnectionFactory connectionFactory = context.getBean(ConnectionFactory.class); + assertThat(connectionFactory).isInstanceOf(RoutingConnectionFactory.class); + final SimpleRoutingConnectionFactory routingConnectionFactory + = (SimpleRoutingConnectionFactory) connectionFactory; + final ConnectionFactory connectionFactory1 = routingConnectionFactory + .getTargetConnectionFactory(broker1); + final ConnectionFactory connectionFactory2 = routingConnectionFactory + .getTargetConnectionFactory(broker2); + assertThat(routingConnectionFactory.getPort()).isEqualTo(5672); + assertThat(connectionFactory1).isNotNull(); + assertThat(connectionFactory1.getPort()).isEqualTo(5673); + assertThat(connectionFactory2).isNotNull(); + assertThat(connectionFactory2.getPort()).isEqualTo(5674); + + final MultiRabbitConnectionFactoryCreator connectionFactoryCreator = context + .getBean(MultiRabbitConnectionFactoryCreator.class); + assertThat(connectionFactoryCreator).isNotNull(); + + final MultiRabbitConnectionFactoryWrapper contextWrapper = context + .getBean(MultiRabbitConnectionFactoryWrapper.class); + assertThat(contextWrapper).isNotNull(); + }); } @Test - void shouldInstantiateDefaultRabbitAdmin() { - assertTrue(config().amqpAdmin(connectionFactory) instanceof RabbitAdmin); + @DisplayName("should ensure MultiRabbit AnnotationBeanPostProcessor") + void shouldEnsureBPP() { + final String broker1 = ThreeListenersBeans.BROKER_NAME_1; + final String broker2 = ThreeListenersBeans.BROKER_NAME_2; + this.contextRunner + .withPropertyValues("spring.multirabbitmq.enabled=true") + .withPropertyValues("spring.multirabbitmq.connections." + broker1 + ".port=5673") + .withPropertyValues("spring.multirabbitmq.connections." + broker2 + ".port=5674") + .withBean(ThreeListenersBeans.class) + .run((context) -> { + final Object annotationBPP = context + .getBean(RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME); + assertThat(annotationBPP).isInstanceOf(MultiRabbitListenerAnnotationBeanPostProcessor.class); + }); } @Test - void shouldInstantiateRabbitConnectionFactoryCreator() { - assertNotNull(config().rabbitConnectionFactoryCreator()); + @DisplayName("should ensure MultiRabbit declarables from Listeners") + void shouldEnsureListenersDeclarables() { + final String broker1 = ThreeListenersBeans.BROKER_NAME_1; + final String broker2 = ThreeListenersBeans.BROKER_NAME_2; + this.contextRunner + .withPropertyValues("spring.multirabbitmq.enabled=true") + .withPropertyValues("spring.multirabbitmq.connections." + broker1 + ".port=5673") + .withPropertyValues("spring.multirabbitmq.connections." + broker2 + ".port=5674") + .withBean(ThreeListenersBeans.class) + .run((context) -> { + assertQueues(context); + assertBindings(context); + assertExchanges(context); + }); } + + @Test + @DisplayName("should fail to initialize listeners when MultiRabbit is disabled") + void shouldFailToInitializeListenersWhenDisabled() { + final ThrowingCallable callable = () -> new AnnotationConfigApplicationContext(ThreeListenersBeans.class, + RabbitAutoConfiguration.class, MultiRabbitAutoConfiguration.class); + assertThatThrownBy(callable).isInstanceOf(BeanCreationException.class); + } + + private void assertExchanges(final AssertableApplicationContext context) { + final Map exchanges = mapToExchangeName(context + .getBeansOfType(DirectExchange.class).values()); + assertThat(exchanges.keySet()).containsExactlyInAnyOrder( + ThreeListenersBeans.EXCHANGE_0, + ThreeListenersBeans.EXCHANGE_1, + ThreeListenersBeans.EXCHANGE_2); + assertAdmin(exchanges.get(ThreeListenersBeans.EXCHANGE_0), + MultiRabbitConstants.DEFAULT_RABBIT_ADMIN_BEAN_NAME); + assertAdmin(exchanges.get(ThreeListenersBeans.EXCHANGE_1), + ThreeListenersBeans.BROKER_NAME_1.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX)); + assertAdmin(exchanges.get(ThreeListenersBeans.EXCHANGE_2), + ThreeListenersBeans.BROKER_NAME_2.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX)); + } + + private void assertBindings(final AssertableApplicationContext context) { + final Map bindings = mapToRoutingKey(context.getBeansOfType(Binding.class).values()); + assertThat(bindings.keySet()).containsExactlyInAnyOrder( + ThreeListenersBeans.ROUTING_KEY_0, + ThreeListenersBeans.ROUTING_KEY_1, + ThreeListenersBeans.ROUTING_KEY_2); + assertAdmin(bindings.get(ThreeListenersBeans.ROUTING_KEY_0), + MultiRabbitConstants.DEFAULT_RABBIT_ADMIN_BEAN_NAME); + assertAdmin(bindings.get(ThreeListenersBeans.ROUTING_KEY_1), + ThreeListenersBeans.BROKER_NAME_1.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX)); + assertAdmin(bindings.get(ThreeListenersBeans.ROUTING_KEY_2), + ThreeListenersBeans.BROKER_NAME_2.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX)); + } + + private void assertQueues(final AssertableApplicationContext context) { + final Map queues = mapToQueueName(context + .getBeansOfType(org.springframework.amqp.core.Queue.class).values()); + assertThat(queues.keySet()).containsExactlyInAnyOrder( + ThreeListenersBeans.QUEUE_0, + ThreeListenersBeans.QUEUE_1, + ThreeListenersBeans.QUEUE_2); + assertAdmin(queues.get(ThreeListenersBeans.QUEUE_0), + MultiRabbitConstants.DEFAULT_RABBIT_ADMIN_BEAN_NAME); + assertAdmin(queues.get(ThreeListenersBeans.QUEUE_1), + ThreeListenersBeans.BROKER_NAME_1.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX)); + assertAdmin(queues.get(ThreeListenersBeans.QUEUE_2), + ThreeListenersBeans.BROKER_NAME_2.concat(MultiRabbitConstants.RABBIT_ADMIN_SUFFIX)); + } + + private void assertAdmin(final Declarable declarable, final String admin) { + assertThat((Collection) declarable.getDeclaringAdmins()).contains(admin); + } + + private Map mapToExchangeName(final Collection exchanges) { + return exchanges.stream().collect(Collectors.toMap(AbstractExchange::getName, v -> v)); + } + + private Map mapToQueueName( + final Collection queues) { + return queues.stream().collect(Collectors.toMap(org.springframework.amqp.core.Queue::getName, v -> v)); + + } + + private Map mapToRoutingKey(final Collection bindings) { + return bindings.stream().collect(Collectors.toMap(Binding::getRoutingKey, v -> v)); + } + + @Component + @EnableRabbit + private static class ThreeListenersBeans { + + public static final String EXCHANGE_0 = "exchange0"; + + public static final String ROUTING_KEY_0 = "routingKey0"; + + public static final String QUEUE_0 = "queue0"; + + public static final String BROKER_NAME_1 = "broker1"; + + public static final String EXCHANGE_1 = "exchange1"; + + public static final String ROUTING_KEY_1 = "routingKey1"; + + public static final String QUEUE_1 = "queue1"; + + public static final String BROKER_NAME_2 = "broker2"; + + public static final String EXCHANGE_2 = "exchange2"; + + public static final String ROUTING_KEY_2 = "routingKey2"; + + public static final String QUEUE_2 = "queue2"; + + @RabbitListener(bindings = @QueueBinding(exchange = @Exchange(EXCHANGE_0), value = @Queue(QUEUE_0), + key = ROUTING_KEY_0)) + void listenBroker0(final String message) { + } + + @RabbitListener(containerFactory = BROKER_NAME_1, bindings = @QueueBinding(exchange = @Exchange(EXCHANGE_1), + value = @Queue(QUEUE_1), key = ROUTING_KEY_1)) + void listenBroker1(final String message) { + } + + @RabbitListener(containerFactory = BROKER_NAME_2, bindings = @QueueBinding(exchange = @Exchange(EXCHANGE_2), + value = @Queue(QUEUE_2), key = ROUTING_KEY_2)) + void listenBroker2(final String message) { + } + + } + } diff --git a/spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitConnectionFactoryCreatorTest.java b/spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitConnectionFactoryCreatorTest.java index 1273d69..607056d 100644 --- a/spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitConnectionFactoryCreatorTest.java +++ b/spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitConnectionFactoryCreatorTest.java @@ -1,5 +1,7 @@ package org.springframework.boot.autoconfigure.amqp; +import com.rabbitmq.client.impl.CredentialsProvider; +import com.rabbitmq.client.impl.CredentialsRefreshService; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.function.Executable; @@ -16,6 +18,7 @@ import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.ApplicationContext; +import org.springframework.core.io.ResourceLoader; import static java.util.Collections.singletonMap; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -43,6 +46,15 @@ class MultiRabbitConnectionFactoryCreatorTest { @Mock private ObjectProvider connectionNameStrategy; + @Mock + private ResourceLoader resourceLoader; + + @Mock + private ObjectProvider credentialsProvider; + + @Mock + private ObjectProvider credentialsRefreshService; + @Mock private MultiRabbitConnectionFactoryWrapper wrapper; @@ -72,8 +84,7 @@ class MultiRabbitConnectionFactoryCreatorTest { private MultiRabbitAutoConfiguration.MultiRabbitConnectionFactoryCreator creator() { MultiRabbitAutoConfiguration.MultiRabbitConnectionFactoryCreator config - = new MultiRabbitAutoConfiguration.MultiRabbitConnectionFactoryCreator(springFactoryCreator, - connectionNameStrategy); + = new MultiRabbitAutoConfiguration.MultiRabbitConnectionFactoryCreator(springFactoryCreator); config.setBeanFactory(beanFactory); config.setApplicationContext(applicationContext); return config; @@ -88,10 +99,12 @@ void shouldInstantiateExternalEmptyWrapper() { @Test void shouldInstantiateRoutingConnectionFactory() throws Exception { - when(springFactoryCreator.rabbitConnectionFactory(rabbitProperties, connectionNameStrategy)) - .thenReturn(new CachingConnectionFactory()); + when(springFactoryCreator.rabbitConnectionFactory(rabbitProperties, resourceLoader, credentialsProvider, + credentialsRefreshService, connectionNameStrategy)).thenReturn(new CachingConnectionFactory()); - assertTrue(creator().routingConnectionFactory(rabbitProperties, multiRabbitProperties, wrapper) + assertTrue(creator().routingConnectionFactory(rabbitProperties, multiRabbitProperties, wrapper, + resourceLoader, + credentialsProvider, credentialsRefreshService, connectionNameStrategy) instanceof RoutingConnectionFactory); } @@ -103,7 +116,8 @@ void shouldInstantiateRoutingConnectionFactoryWithDefaultAndMultipleConnections( when(wrapper.getRabbitAdmins()).thenReturn(singletonMap(DUMMY_KEY, rabbitAdmin)); ConnectionFactory routingConnectionFactory = creator().routingConnectionFactory(rabbitProperties, - multiRabbitProperties, wrapper); + multiRabbitProperties, wrapper, resourceLoader, credentialsProvider, credentialsRefreshService, + connectionNameStrategy); assertTrue(routingConnectionFactory instanceof SimpleRoutingConnectionFactory); verify(beanFactory).registerSingleton(DUMMY_KEY, containerFactory); @@ -117,7 +131,8 @@ void shouldInstantiateRoutingConnectionFactoryWithOnlyDefaultConnectionFactory() when(wrapper.getDefaultConnectionFactory()).thenReturn(connectionFactory0); ConnectionFactory routingConnectionFactory = creator().routingConnectionFactory(rabbitProperties, - multiRabbitProperties, wrapper); + multiRabbitProperties, wrapper, resourceLoader, credentialsProvider, credentialsRefreshService, + connectionNameStrategy); assertTrue(routingConnectionFactory instanceof SimpleRoutingConnectionFactory); verifyNoMoreInteractions(beanFactory); @@ -128,11 +143,12 @@ void shouldInstantiateRoutingConnectionFactoryWithOnlyMultipleConnectionFactorie when(wrapper.getConnectionFactories()).thenReturn(singletonMap(DUMMY_KEY, connectionFactory0)); when(wrapper.getContainerFactories()).thenReturn(singletonMap(DUMMY_KEY, containerFactory)); when(wrapper.getRabbitAdmins()).thenReturn(singletonMap(DUMMY_KEY, rabbitAdmin)); - when(springFactoryCreator.rabbitConnectionFactory(rabbitProperties, connectionNameStrategy)) - .thenReturn(new CachingConnectionFactory()); + when(springFactoryCreator.rabbitConnectionFactory(rabbitProperties, resourceLoader, credentialsProvider, + credentialsRefreshService, connectionNameStrategy)).thenReturn(new CachingConnectionFactory()); ConnectionFactory routingConnectionFactory = creator().routingConnectionFactory(rabbitProperties, - multiRabbitProperties, wrapper); + multiRabbitProperties, wrapper, resourceLoader, credentialsProvider, credentialsRefreshService, + connectionNameStrategy); assertTrue(routingConnectionFactory instanceof SimpleRoutingConnectionFactory); verify(beanFactory).registerSingleton(DUMMY_KEY, containerFactory); @@ -148,7 +164,8 @@ void shouldReachDefaultConnectionFactoryWhenNotBound() throws Exception { when(wrapper.getContainerFactories()).thenReturn(singletonMap(DUMMY_KEY, containerFactory)); when(wrapper.getRabbitAdmins()).thenReturn(singletonMap(DUMMY_KEY, rabbitAdmin)); - creator().routingConnectionFactory(rabbitProperties, multiRabbitProperties, wrapper).getVirtualHost(); + creator().routingConnectionFactory(rabbitProperties, multiRabbitProperties, wrapper, resourceLoader, + credentialsProvider, credentialsRefreshService, connectionNameStrategy).getVirtualHost(); verify(connectionFactory0).getVirtualHost(); verify(connectionFactory1, never()).getVirtualHost(); @@ -162,7 +179,8 @@ void shouldBindAndReachMultiConnectionFactory() throws Exception { when(wrapper.getRabbitAdmins()).thenReturn(singletonMap(DUMMY_KEY, rabbitAdmin)); ConnectionFactory routingConnectionFactory = creator().routingConnectionFactory(rabbitProperties, - multiRabbitProperties, wrapper); + multiRabbitProperties, wrapper, resourceLoader, credentialsProvider, credentialsRefreshService, + connectionNameStrategy); SimpleResourceHolder.bind(routingConnectionFactory, DUMMY_KEY); routingConnectionFactory.getVirtualHost(); @@ -174,51 +192,60 @@ void shouldBindAndReachMultiConnectionFactory() throws Exception { @Test void shouldInstantiateMultiRabbitConnectionFactoryWrapperWithDefaultConnection() throws Exception { - when(springFactoryCreator.rabbitConnectionFactory(rabbitProperties, connectionNameStrategy)) - .thenReturn(new CachingConnectionFactory()); + when(springFactoryCreator.rabbitConnectionFactory(rabbitProperties, resourceLoader, credentialsProvider, + credentialsRefreshService, connectionNameStrategy)).thenReturn(new CachingConnectionFactory()); - assertNotNull(creator().routingConnectionFactory(rabbitProperties, null, wrapper)); + assertNotNull(creator().routingConnectionFactory(rabbitProperties, null, wrapper, + resourceLoader, credentialsProvider, credentialsRefreshService, connectionNameStrategy)); } @Test void shouldInstantiateMultiRabbitConnectionFactoryWrapperWithMultipleConnections() throws Exception { - when(springFactoryCreator.rabbitConnectionFactory(any(RabbitProperties.class), eq(connectionNameStrategy))) + when(springFactoryCreator.rabbitConnectionFactory(any(RabbitProperties.class), eq(resourceLoader), + eq(credentialsProvider), eq(credentialsRefreshService), eq(connectionNameStrategy))) .thenReturn(new CachingConnectionFactory()); MultiRabbitProperties multiRabbitProperties = new MultiRabbitProperties(); multiRabbitProperties.getConnections().put(DUMMY_KEY, secondaryRabbitProperties); multiRabbitProperties.setDefaultConnection(DUMMY_KEY); - creator().routingConnectionFactory(null, multiRabbitProperties, wrapper); + creator().routingConnectionFactory(null, multiRabbitProperties, wrapper, resourceLoader, + credentialsProvider, credentialsRefreshService, connectionNameStrategy); - verify(springFactoryCreator).rabbitConnectionFactory(secondaryRabbitProperties, connectionNameStrategy); + verify(springFactoryCreator).rabbitConnectionFactory(secondaryRabbitProperties, resourceLoader, + credentialsProvider, credentialsRefreshService, connectionNameStrategy); } @Test void shouldInstantiateMultiRabbitConnectionFactoryWrapperWithDefaultAndMultipleConnections() throws Exception { - when(springFactoryCreator.rabbitConnectionFactory(any(RabbitProperties.class), eq(connectionNameStrategy))) + when(springFactoryCreator.rabbitConnectionFactory(any(RabbitProperties.class), eq(resourceLoader), + eq(credentialsProvider), eq(credentialsRefreshService), eq(connectionNameStrategy))) .thenReturn(new CachingConnectionFactory()); MultiRabbitProperties multiRabbitProperties = new MultiRabbitProperties(); multiRabbitProperties.getConnections().put(DUMMY_KEY, secondaryRabbitProperties); - creator().routingConnectionFactory(rabbitProperties, multiRabbitProperties, wrapper); + creator().routingConnectionFactory(rabbitProperties, multiRabbitProperties, wrapper, resourceLoader, + credentialsProvider, credentialsRefreshService, connectionNameStrategy); - verify(springFactoryCreator).rabbitConnectionFactory(rabbitProperties, connectionNameStrategy); - verify(springFactoryCreator).rabbitConnectionFactory(secondaryRabbitProperties, connectionNameStrategy); + verify(springFactoryCreator).rabbitConnectionFactory(rabbitProperties, resourceLoader, credentialsProvider, + credentialsRefreshService, connectionNameStrategy); + verify(springFactoryCreator).rabbitConnectionFactory(secondaryRabbitProperties, resourceLoader, + credentialsProvider, credentialsRefreshService, connectionNameStrategy); } @Test void shouldEncapsulateExceptionWhenFailingToCreateBean() throws Exception { - when(springFactoryCreator.rabbitConnectionFactory(any(RabbitProperties.class), eq(connectionNameStrategy))) + when(springFactoryCreator.rabbitConnectionFactory(any(RabbitProperties.class), eq(resourceLoader), + eq(credentialsProvider), eq(credentialsRefreshService), eq(connectionNameStrategy))) .thenThrow(new Exception("mocked-exception")); MultiRabbitProperties multiRabbitProperties = new MultiRabbitProperties(); multiRabbitProperties.getConnections().put(DUMMY_KEY, secondaryRabbitProperties); final Executable executable = () -> creator().routingConnectionFactory(rabbitProperties, multiRabbitProperties, - wrapper); + wrapper, resourceLoader, credentialsProvider, credentialsRefreshService, connectionNameStrategy); assertThrows(Exception.class, executable, "mocked-exception"); } diff --git a/spring-multirabbit/src/test/resources/application-three-brokers.properties b/spring-multirabbit/src/test/resources/application-three-brokers.properties deleted file mode 100644 index af9225d..0000000 --- a/spring-multirabbit/src/test/resources/application-three-brokers.properties +++ /dev/null @@ -1,4 +0,0 @@ -spring.rabbitmq.port=5672 -spring.multirabbitmq.enabled=true -spring.multirabbitmq.connections.broker1.port=5673 -spring.multirabbitmq.connections.broker2.port=5674 diff --git a/spring-multirabbit/src/test/resources/application.yml b/spring-multirabbit/src/test/resources/application.yml deleted file mode 100644 index a91afe2..0000000 --- a/spring-multirabbit/src/test/resources/application.yml +++ /dev/null @@ -1,11 +0,0 @@ -spring: - rabbitmq: - host: localhost - port: 5672 - multirabbitmq: - connectionNameA: - host: localhost - port: 5673 - connectionNameB: - host: localhost - port: 5674 \ No newline at end of file From c4719a8e4564f27d9944042b71ef6a5b2b838ba9 Mon Sep 17 00:00:00 2001 From: Wander Costa Date: Sat, 6 Mar 2021 23:03:27 +0100 Subject: [PATCH 3/4] GH-35 Updated documentation --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3366817..264f76b 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ **Spring MultiRabbit** is a library to enable multiple RabbitMQ brokers in SpringBoot applications. The modules are: * **spring-multirabbit** - the main module, that provides the auto-configuration feature; -* **spring-multirabbit-integration** - a module to test integration with Spring; * **spring-multirabbit-example-java** - an example project in Java; * **spring-multirabbit-example-kotlin** - an example project in Kotlin; * **spring-multirabbit-extension-example** - an example project of how to extend spring-multirabbit; @@ -14,8 +13,9 @@ To use the library, the project must: 1. Be a SpringBoot project annotated with **@EnableRabbit**, as usual; 2. Import the library **spring-multirabbit**; -3. Provide configuration for additional brokers in the new path **spring.multirabbitmq.connections**. All - attributes available for **spring.rabbitmq** can be used in **spring.multirabbitmq.connections**. +3. Enable MultiRabbit by setting **spring.multirabbitmq.enabled=true**. By default, MultiRabbit is disabled. +3. Provide configuration for additional brokers under **spring.multirabbitmq.connections**. All + attributes available for **spring.rabbitmq** can be used under **spring.multirabbitmq.connections**. 4. Change the container factory context when using non-default connections: 1. For ```RabbitTemplate```, use ```SimpleResourceHolder.bind()``` and ```SimpleResourceHolder.unbind()```; 2. For ```@RabbitListener```, define the ```containerFactory``` or leave it blank for the default connection. @@ -54,6 +54,7 @@ spring: host: 10.0.0.10 port: 5672 multirabbitmq: + enabled: true connections: connectionNameA: host: 200.10.10.10 @@ -131,6 +132,7 @@ any of the connections under **spring.multirabbitmq.connections**, the connectio ```yaml spring: multirabbitmq: + enabled: true connections: connectionNameA: host: localhost @@ -147,6 +149,7 @@ the default connection. ```yaml spring: multirabbitmq: + enabled: true defaultConnection: connectionNameA connections: connectionNameA: @@ -165,6 +168,7 @@ spring: host: localhost port: 5672 multirabbitmq: + enabled: true connections: connectionNameA: host: localhost @@ -184,6 +188,7 @@ spring: host: localhost port: 5672 multirabbitmq: + enabled: true connections: connectionNameA: defaultConnection: true From 6afac1f8c5ab2c19d4cfbae660f620adebcedf5e Mon Sep 17 00:00:00 2001 From: Wander Costa Date: Sun, 7 Mar 2021 10:16:24 +0100 Subject: [PATCH 4/4] GH-35 Simplified bean creation by importing RabbitAutoConfiguration --- .../amqp/MultiRabbitAutoConfiguration.java | 19 ++----------------- .../MultiRabbitAutoConfigurationTest.java | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/spring-multirabbit/src/main/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfiguration.java b/spring-multirabbit/src/main/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfiguration.java index 694f530..2337361 100644 --- a/spring-multirabbit/src/main/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfiguration.java +++ b/spring-multirabbit/src/main/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfiguration.java @@ -7,7 +7,6 @@ import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.amqp.core.AmqpAdmin; import org.springframework.amqp.rabbit.annotation.MultiRabbitBootstrapConfiguration; import org.springframework.amqp.rabbit.config.AbstractRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; @@ -25,7 +24,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -45,25 +43,11 @@ @Configuration @ConditionalOnClass({RabbitTemplate.class, Channel.class}) @EnableConfigurationProperties({RabbitProperties.class, MultiRabbitProperties.class}) -@Import({MultiRabbitBootstrapConfiguration.class, RabbitAnnotationDrivenConfiguration.class}) +@Import({MultiRabbitBootstrapConfiguration.class, RabbitAutoConfiguration.class}) public class MultiRabbitAutoConfiguration { private static final Logger LOGGER = LoggerFactory.getLogger(MultiRabbitAutoConfiguration.class); - /** - * Creates an {@link AmqpAdmin}. - * - * @param connectionFactory The {@link ConnectionFactory} to be associated to. - * @return an {@link AmqpAdmin}. - */ - @Bean(MultiRabbitConstants.DEFAULT_RABBIT_ADMIN_BEAN_NAME) - @Primary - @ConditionalOnSingleCandidate(ConnectionFactory.class) - @ConditionalOnProperty(prefix = "spring.rabbitmq", name = "dynamic", matchIfMissing = true) - public AmqpAdmin amqpAdmin(final ConnectionFactory connectionFactory) { - return new RabbitAdmin(connectionFactory); - } - /** * Returns a {@link RabbitConnectionFactoryCreator}. * @@ -71,6 +55,7 @@ public AmqpAdmin amqpAdmin(final ConnectionFactory connectionFactory) { */ @Primary @Bean(MultiRabbitConstants.CONNECTION_FACTORY_CREATOR_BEAN_NAME) + @ConditionalOnProperty(prefix = "spring.multirabbitmq", name = "enabled", havingValue = "true") public RabbitConnectionFactoryCreator rabbitConnectionFactoryCreator() { return new RabbitConnectionFactoryCreator(); } diff --git a/spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfigurationTest.java b/spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfigurationTest.java index cce5fc0..4eda821 100644 --- a/spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfigurationTest.java +++ b/spring-multirabbit/src/test/java/org/springframework/boot/autoconfigure/amqp/MultiRabbitAutoConfigurationTest.java @@ -83,9 +83,15 @@ void shouldEnsureMultiRabbitNotLoadedWhenDisabled() { assertThat(annotationBPP).isNotInstanceOf(MultiRabbitListenerAnnotationBeanPostProcessor.class); final ThrowingCallable connectionFactoryCreator = () -> context - .getBean(MultiRabbitConnectionFactoryCreator.class); + .getBean(MultiRabbitConstants.CONNECTION_FACTORY_CREATOR_BEAN_NAME, + RabbitAutoConfiguration.RabbitConnectionFactoryCreator.class); assertThatThrownBy(connectionFactoryCreator).isInstanceOf(NoSuchBeanDefinitionException.class); + final ThrowingCallable multiRabbitConnectionFactoryCreator = () -> context + .getBean(MultiRabbitConnectionFactoryCreator.class); + assertThatThrownBy(multiRabbitConnectionFactoryCreator) + .isInstanceOf(NoSuchBeanDefinitionException.class); + final ThrowingCallable contextWrapper = () -> context .getBean(MultiRabbitConnectionFactoryWrapper.class); assertThatThrownBy(contextWrapper).isInstanceOf(NoSuchBeanDefinitionException.class); @@ -137,10 +143,15 @@ void shouldEnsureAutoConfigurationBeans() { assertThat(connectionFactory2).isNotNull(); assertThat(connectionFactory2.getPort()).isEqualTo(5674); - final MultiRabbitConnectionFactoryCreator connectionFactoryCreator = context - .getBean(MultiRabbitConnectionFactoryCreator.class); + final RabbitAutoConfiguration.RabbitConnectionFactoryCreator connectionFactoryCreator = context + .getBean(MultiRabbitConstants.CONNECTION_FACTORY_CREATOR_BEAN_NAME, + RabbitAutoConfiguration.RabbitConnectionFactoryCreator.class); assertThat(connectionFactoryCreator).isNotNull(); + final MultiRabbitConnectionFactoryCreator multiRabbitConnectionFactoryCreator = context + .getBean(MultiRabbitConnectionFactoryCreator.class); + assertThat(multiRabbitConnectionFactoryCreator).isNotNull(); + final MultiRabbitConnectionFactoryWrapper contextWrapper = context .getBean(MultiRabbitConnectionFactoryWrapper.class); assertThat(contextWrapper).isNotNull();