/* * Copyright 2014-2016 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.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import java.util.Collection; import java.util.Map; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.rabbit.config.RabbitListenerContainerTestFactory; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerEndpoint; import org.springframework.amqp.rabbit.listener.AbstractRabbitListenerEndpoint; import org.springframework.amqp.rabbit.listener.MethodRabbitListenerEndpoint; import org.springframework.amqp.rabbit.listener.RabbitListenerEndpoint; import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import org.springframework.validation.Errors; import org.springframework.validation.Validator; import org.springframework.validation.annotation.Validated; import com.rabbitmq.client.Channel; /** * * @author Stephane Nicoll * @author Gary Russell */ public abstract class AbstractRabbitAnnotationDrivenTests { @Rule public final ExpectedException thrown = ExpectedException.none(); @Test public abstract void sampleConfiguration(); @Test public abstract void fullConfiguration(); @Test public abstract void fullConfigurableConfiguration(); @Test public abstract void noRabbitAdminConfiguration(); @Test public abstract void customConfiguration(); @Test public abstract void explicitContainerFactory(); @Test public abstract void defaultContainerFactory(); @Test public abstract void rabbitHandlerMethodFactoryConfiguration() throws Exception; @Test public abstract void rabbitListeners(); /** * Test for {@link SampleBean} discovery. If a factory with the default name * is set, an endpoint will use it automatically */ public void testSampleConfiguration(ApplicationContext context, int expectedDefaultContainers) { RabbitListenerContainerTestFactory defaultFactory = context.getBean("rabbitListenerContainerFactory", RabbitListenerContainerTestFactory.class); RabbitListenerContainerTestFactory simpleFactory = context.getBean("simpleFactory", RabbitListenerContainerTestFactory.class); assertEquals(expectedDefaultContainers, defaultFactory.getListenerContainers().size()); assertEquals(1, simpleFactory.getListenerContainers().size()); Map<String, org.springframework.amqp.core.Queue> queues = context .getBeansOfType(org.springframework.amqp.core.Queue.class); for (org.springframework.amqp.core.Queue queue : queues.values()) { assertTrue(queue.isIgnoreDeclarationExceptions()); } Map<String, org.springframework.amqp.core.Exchange> exchanges = context .getBeansOfType(org.springframework.amqp.core.Exchange.class); for (org.springframework.amqp.core.Exchange exchange : exchanges.values()) { assertTrue(exchange.isIgnoreDeclarationExceptions()); } Map<String, org.springframework.amqp.core.Binding> bindings = context .getBeansOfType(org.springframework.amqp.core.Binding.class); for (org.springframework.amqp.core.Binding binding : bindings.values()) { assertTrue(binding.isIgnoreDeclarationExceptions()); } } /** * Test for {@link FullBean} discovery. In this case, no default is set because * all endpoints provide a default registry. This shows that the default factory * is only retrieved if it needs to be. */ public void testFullConfiguration(ApplicationContext context) { RabbitListenerContainerTestFactory simpleFactory = context.getBean("simpleFactory", RabbitListenerContainerTestFactory.class); assertEquals(1, simpleFactory.getListenerContainers().size()); MethodRabbitListenerEndpoint endpoint = (MethodRabbitListenerEndpoint) simpleFactory.getListenerContainers().get(0).getEndpoint(); assertEquals("listener1", endpoint.getId()); assertQueues(endpoint, "queue1", "queue2"); assertTrue("No queue instances should be set", endpoint.getQueues().isEmpty()); assertEquals(true, endpoint.isExclusive()); assertEquals(new Integer(34), endpoint.getPriority()); assertSame(context.getBean("rabbitAdmin"), endpoint.getAdmin()); // Resolve the container and invoke a message on it SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); endpoint.setupListenerContainer(container); MessagingMessageListenerAdapter listener = (MessagingMessageListenerAdapter) container.getMessageListener(); MessageProperties properties = new MessageProperties(); properties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN); Message amqpMessage = new Message("Hello".getBytes(), properties); try { listener.onMessage(amqpMessage, mock(Channel.class)); } catch (Exception e) { fail("should not have failed to process simple message but got " + e.getMessage()); } } /** * Test for {@link CustomBean} and an manually endpoint registered * with "myCustomEndpointId". The custom endpoint does not provide * any factory so it's registered with the default one */ public void testCustomConfiguration(ApplicationContext context) { RabbitListenerContainerTestFactory defaultFactory = context.getBean("rabbitListenerContainerFactory", RabbitListenerContainerTestFactory.class); RabbitListenerContainerTestFactory customFactory = context.getBean("customFactory", RabbitListenerContainerTestFactory.class); assertEquals(1, defaultFactory.getListenerContainers().size()); assertEquals(1, customFactory.getListenerContainers().size()); RabbitListenerEndpoint endpoint = defaultFactory.getListenerContainers().get(0).getEndpoint(); assertEquals("Wrong endpoint type", SimpleRabbitListenerEndpoint.class, endpoint.getClass()); assertEquals("Wrong listener set in custom endpoint", context.getBean("simpleMessageListener"), ((SimpleRabbitListenerEndpoint) endpoint).getMessageListener()); RabbitListenerEndpointRegistry customRegistry = context.getBean("customRegistry", RabbitListenerEndpointRegistry.class); assertEquals("Wrong number of containers in the registry", 2, customRegistry.getListenerContainerIds().size()); assertEquals("Wrong number of containers in the registry", 2, customRegistry.getListenerContainers().size()); assertNotNull("Container with custom id on the annotation should be found", customRegistry.getListenerContainer("listenerId")); assertNotNull("Container created with custom id should be found", customRegistry.getListenerContainer("myCustomEndpointId")); } /** * Test for {@link DefaultBean} that does not define the container * factory to use as a default is registered with an explicit * default. */ public void testExplicitContainerFactoryConfiguration(ApplicationContext context) { RabbitListenerContainerTestFactory defaultFactory = context.getBean("simpleFactory", RabbitListenerContainerTestFactory.class); assertEquals(1, defaultFactory.getListenerContainers().size()); } /** * Test for {@link DefaultBean} that does not define the container * factory to use as a default is registered with the default name. */ public void testDefaultContainerFactoryConfiguration(ApplicationContext context) { RabbitListenerContainerTestFactory defaultFactory = context.getBean("rabbitListenerContainerFactory", RabbitListenerContainerTestFactory.class); assertEquals(1, defaultFactory.getListenerContainers().size()); } /** * Test for {@link ValidationBean} with a validator ({@link TestValidator}) specified * in a custom {@link org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory}. * * The test should throw a {@link org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException} */ public void testRabbitHandlerMethodFactoryConfiguration(ApplicationContext context) throws Exception { RabbitListenerContainerTestFactory simpleFactory = context.getBean("defaultFactory", RabbitListenerContainerTestFactory.class); assertEquals(1, simpleFactory.getListenerContainers().size()); MethodRabbitListenerEndpoint endpoint = (MethodRabbitListenerEndpoint) simpleFactory.getListenerContainers().get(0).getEndpoint(); SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); endpoint.setupListenerContainer(container); MessagingMessageListenerAdapter listener = (MessagingMessageListenerAdapter) container.getMessageListener(); MessageProperties properties = new MessageProperties(); properties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN); Message amqpMessage = new Message("failValidation".getBytes(), properties); listener.onMessage(amqpMessage, mock(Channel.class)); } /** * Test for {@link RabbitListenersBean} that validates that the * {@code @RabbitListener} annotations generate one specific container per annotation. */ public void testRabbitListenerRepeatable(ApplicationContext context) { RabbitListenerContainerTestFactory simpleFactory = context.getBean("rabbitListenerContainerFactory", RabbitListenerContainerTestFactory.class); assertEquals(4, simpleFactory.getListenerContainers().size()); MethodRabbitListenerEndpoint first = (MethodRabbitListenerEndpoint) simpleFactory.getListenerContainer("first").getEndpoint(); assertEquals("first", first.getId()); assertEquals("myQueue", first.getQueueNames().iterator().next()); MethodRabbitListenerEndpoint second = (MethodRabbitListenerEndpoint) simpleFactory.getListenerContainer("second").getEndpoint(); assertEquals("second", second.getId()); assertEquals("anotherQueue", second.getQueueNames().iterator().next()); MethodRabbitListenerEndpoint third = (MethodRabbitListenerEndpoint) simpleFactory.getListenerContainer("third").getEndpoint(); assertEquals("third", third.getId()); assertEquals("class1", third.getQueueNames().iterator().next()); MethodRabbitListenerEndpoint fourth = (MethodRabbitListenerEndpoint) simpleFactory.getListenerContainer("fourth").getEndpoint(); assertEquals("fourth", fourth.getId()); assertEquals("class2", fourth.getQueueNames().iterator().next()); } private void assertQueues(AbstractRabbitListenerEndpoint actual, String... expectedQueues) { Collection<String> actualQueues = actual.getQueueNames(); for (String expectedQueue : expectedQueues) { assertTrue("Queue '" + expectedQueue + "' not found", actualQueues.contains(expectedQueue)); } assertEquals("Wrong number of queues", expectedQueues.length, actualQueues.size()); } @Component static class SampleBean { @RabbitListener(queues = "myQueue") public void defaultHandle(String msg) { } @RabbitListener(containerFactory = "simpleFactory", queues = "myQueue") public void simpleHandle(String msg) { } } @Component static class FullBean { @RabbitListener(id = "listener1", containerFactory = "simpleFactory", queues = {"queue1", "queue2"}, exclusive = true, priority = "34", admin = "rabbitAdmin") public void fullHandle(String msg) { } } @Component static class FullConfigurableBean { @RabbitListener(id = "${rabbit.listener.id}", containerFactory = "${rabbit.listener.containerFactory}", queues = {"${rabbit.listener.queue}", "queue2"}, exclusive = true, priority = "${rabbit.listener.priority}", admin = "${rabbit.listener.admin}") public void fullHandle(String msg) { } } @Component static class CustomBean { @RabbitListener(id = "listenerId", containerFactory = "customFactory", queues = "myQueue") public void customHandle(String msg) { } } static class DefaultBean { @RabbitListener(queues = "myQueue") public void handleIt(String msg) { } } @Component static class ValidationBean { @RabbitListener(containerFactory = "defaultFactory", queues = "myQueue") public void defaultHandle(@Validated String msg) { } } @Component static class RabbitListenersBean { @RabbitListeners({ @RabbitListener(id = "first", queues = "myQueue"), @RabbitListener(id = "second", queues = "anotherQueue") }) public void repeatableHandle(String msg) { } } @Component @RabbitListeners({ @RabbitListener(id = "third", queues = "class1"), @RabbitListener(id = "fourth", queues = "class2") }) static class ClassLevelListenersBean { @RabbitHandler public void repeatableHandle(String msg) { } } static class TestValidator implements Validator { @Override public boolean supports(Class<?> clazz) { return String.class.isAssignableFrom(clazz); } @Override public void validate(Object target, Errors errors) { String value = (String) target; if ("failValidation".equals(value)) { errors.reject("TEST: expected invalid value"); } } } }