/* * Copyright 2002-2017 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 * * http://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.amqp.rabbit.annotation; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import org.hamcrest.core.Is; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.amqp.core.MessageListener; import org.springframework.amqp.rabbit.config.MessageListenerTestContainer; import org.springframework.amqp.rabbit.config.RabbitListenerContainerTestFactory; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerEndpoint; import org.springframework.amqp.rabbit.connection.Connection; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitAdmin; import org.springframework.amqp.rabbit.listener.MessageListenerContainer; import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar; import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; import org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException; import org.springframework.beans.factory.BeanCreationException; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory; import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory; import org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException; import org.springframework.stereotype.Component; import com.rabbitmq.client.Channel; /** * @author Stephane Nicoll * @author Artem Bilan * @author Gary Russell */ public class EnableRabbitTests extends AbstractRabbitAnnotationDrivenTests { @Rule public final ExpectedException thrown = ExpectedException.none(); @Override @Test public void sampleConfiguration() { ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( EnableRabbitSampleConfig.class, SampleBean.class); testSampleConfiguration(context, 2); } @Override @Test public void fullConfiguration() { ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( EnableRabbitFullConfig.class, FullBean.class); testFullConfiguration(context); } @Override @Test public void fullConfigurableConfiguration() { ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( EnableRabbitFullConfigurableConfig.class, FullConfigurableBean.class); testFullConfiguration(context); } @Override public void noRabbitAdminConfiguration() { thrown.expect(BeanCreationException.class); thrown.expectMessage("'rabbitAdmin'"); new AnnotationConfigApplicationContext(EnableRabbitSampleConfig.class, FullBean.class).close(); } @Override @Test public void customConfiguration() { ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( EnableRabbitCustomConfig.class, CustomBean.class); testCustomConfiguration(context); } @Override @Test public void explicitContainerFactory() { ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( EnableRabbitCustomContainerFactoryConfig.class, DefaultBean.class); testExplicitContainerFactoryConfiguration(context); } @Override @Test public void defaultContainerFactory() { ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( EnableRabbitDefaultContainerFactoryConfig.class, DefaultBean.class); testDefaultContainerFactoryConfiguration(context); } @Override @Test public void rabbitHandlerMethodFactoryConfiguration() throws Exception { ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( EnableRabbitHandlerMethodFactoryConfig.class, ValidationBean.class); thrown.expect(ListenerExecutionFailedException.class); thrown.expectCause(Is.<MethodArgumentNotValidException>isA(MethodArgumentNotValidException.class)); testRabbitHandlerMethodFactoryConfiguration(context); } @Test public void unknownFactory() { thrown.expect(BeanCreationException.class); thrown.expectMessage("customFactory"); // Not found new AnnotationConfigApplicationContext( EnableRabbitSampleConfig.class, CustomBean.class).close(); } @Test public void invalidPriorityConfiguration() { thrown.expect(BeanCreationException.class); thrown.expectMessage("NotANumber"); // Invalid number new AnnotationConfigApplicationContext( EnableRabbitSampleConfig.class, InvalidPriorityBean.class).close(); } @Test public void lazyComponent() { ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( EnableRabbitDefaultContainerFactoryConfig.class, LazyBean.class); RabbitListenerContainerTestFactory defaultFactory = context.getBean("rabbitListenerContainerFactory", RabbitListenerContainerTestFactory.class); assertEquals(0, defaultFactory.getListenerContainers().size()); context.getBean(LazyBean.class); // trigger lazy resolution assertEquals(1, defaultFactory.getListenerContainers().size()); MessageListenerTestContainer container = defaultFactory.getListenerContainers().get(0); assertTrue("Should have been started " + container, container.isStarted()); context.close(); // Close and stop the listeners assertTrue("Should have been stopped " + container, container.isStopped()); } @Override @Test public void rabbitListeners() { ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( EnableRabbitDefaultContainerFactoryConfig.class, RabbitListenersBean.class, ClassLevelListenersBean.class); testRabbitListenerRepeatable(context); } @Test public void testProperShutdownOnException() { ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( ProperShutdownConfig.class, RabbitListenersBean.class, ClassLevelListenersBean.class); RabbitListenerEndpointRegistry listenerEndpointRegistry = context.getBean(RabbitListenerEndpointRegistry.class); // Previously this takes 30 seconds to finish (see DefaultLifecycleProcessor#timeoutPerShutdownPhase) // And not all containers has been stopped from the RabbitListenerEndpointRegistry context.close(); for (MessageListenerContainer messageListenerContainer : listenerEndpointRegistry.getListenerContainers()) { assertFalse(messageListenerContainer.isRunning()); } } @EnableRabbit @Configuration static class EnableRabbitSampleConfig { @Bean public RabbitListenerContainerTestFactory rabbitListenerContainerFactory() { return new RabbitListenerContainerTestFactory(); } @Bean public RabbitListenerContainerTestFactory simpleFactory() { return new RabbitListenerContainerTestFactory(); } @Bean public Listener listener() { return new Listener(); } static class Listener { @RabbitListener(bindings = @QueueBinding(value = @Queue(value = "foo", ignoreDeclarationExceptions = "true"), exchange = @Exchange(value = "bar", ignoreDeclarationExceptions = "true"), key = "baz", ignoreDeclarationExceptions = "true")) public void handle(String foo) { // empty } } } @EnableRabbit @Configuration static class EnableRabbitFullConfig { @Bean public RabbitListenerContainerTestFactory simpleFactory() { return new RabbitListenerContainerTestFactory(); } @Bean public RabbitAdmin rabbitAdmin() { return mock(RabbitAdmin.class); } } @EnableRabbit @Configuration @PropertySource("classpath:/org/springframework/amqp/rabbit/annotation/rabbit-listener.properties") static class EnableRabbitFullConfigurableConfig { @Bean public RabbitListenerContainerTestFactory simpleFactory() { return new RabbitListenerContainerTestFactory(); } @Bean public RabbitAdmin rabbitAdmin() { return mock(RabbitAdmin.class); } @Bean public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } } @Configuration @EnableRabbit static class EnableRabbitCustomConfig implements RabbitListenerConfigurer { @Override public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) { registrar.setEndpointRegistry(customRegistry()); // Also register a custom endpoint SimpleRabbitListenerEndpoint endpoint = new SimpleRabbitListenerEndpoint(); endpoint.setId("myCustomEndpointId"); endpoint.setQueueNames("myQueue"); endpoint.setMessageListener(simpleMessageListener()); registrar.registerEndpoint(endpoint); } @Bean public RabbitListenerContainerTestFactory rabbitListenerContainerFactory() { return new RabbitListenerContainerTestFactory(); } @Bean public RabbitListenerEndpointRegistry customRegistry() { return new RabbitListenerEndpointRegistry(); } @Bean public RabbitListenerContainerTestFactory customFactory() { return new RabbitListenerContainerTestFactory(); } @Bean public MessageListener simpleMessageListener() { return new MessageListenerAdapter(); } } @Configuration @EnableRabbit static class EnableRabbitCustomContainerFactoryConfig implements RabbitListenerConfigurer { @Override public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) { registrar.setContainerFactory(simpleFactory()); } @Bean public RabbitListenerContainerTestFactory simpleFactory() { return new RabbitListenerContainerTestFactory(); } } @Configuration @EnableRabbit static class EnableRabbitDefaultContainerFactoryConfig { @Bean public RabbitListenerContainerTestFactory rabbitListenerContainerFactory() { return new RabbitListenerContainerTestFactory(); } } @Configuration @EnableRabbit static class EnableRabbitHandlerMethodFactoryConfig implements RabbitListenerConfigurer { @Override public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) { registrar.setMessageHandlerMethodFactory(customMessageHandlerMethodFactory()); } @Bean public MessageHandlerMethodFactory customMessageHandlerMethodFactory() { DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory(); factory.setValidator(new TestValidator()); return factory; } @Bean public RabbitListenerContainerTestFactory defaultFactory() { return new RabbitListenerContainerTestFactory(); } } @Configuration @EnableRabbit static class ProperShutdownConfig { @Bean public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() { SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory() { @Override protected SimpleMessageListenerContainer createContainerInstance() { SimpleMessageListenerContainer listenerContainer = spy(super.createContainerInstance()); willThrow(RuntimeException.class) .given(listenerContainer) .shutdown(); return listenerContainer; } }; ConnectionFactory connectionFactory = mock(ConnectionFactory.class); Connection connection = mock(Connection.class); given(connection.createChannel(anyBoolean())) .willReturn(mock(Channel.class)); given(connectionFactory.createConnection()) .willReturn(connection); containerFactory.setConnectionFactory(connectionFactory); return containerFactory; } } @Component static class InvalidPriorityBean { @RabbitListener(queues = "myQueue", priority = "NotANumber") public void customHandle(String msg) { } } @Component @Lazy static class LazyBean { @RabbitListener(queues = "myQueue") public void handle(String msg) { } } }