/*
* Copyright 2014-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.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.config.DirectRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitManagementTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.junit.BrokerRunning;
import org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler;
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.RabbitListenerErrorHandler;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException;
import org.springframework.amqp.rabbit.test.MessageTestUtils;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.amqp.support.ConsumerTagStrategy;
import org.springframework.amqp.support.converter.DefaultClassMapper;
import org.springframework.amqp.support.converter.DefaultJackson2JavaTypeMapper;
import org.springframework.amqp.support.converter.Jackson2JavaTypeMapper.TypePrecedence;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.RemoteInvocationAwareMessageConverterAdapter;
import org.springframework.amqp.support.converter.SimpleMessageConverter;
import org.springframework.amqp.utils.test.TestUtils;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.messaging.converter.GenericMessageConverter;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.TestExecutionListeners.MergeMode;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ErrorHandler;
import com.rabbitmq.client.Channel;
/**
*
* @author Stephane Nicoll
* @author Artem Bilan
* @author Gary Russell
*
* @since 1.4
*/
@ContextConfiguration(classes = EnableRabbitIntegrationTests.EnableRabbitConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
@TestExecutionListeners(mergeMode = MergeMode.MERGE_WITH_DEFAULTS,
listeners = EnableRabbitIntegrationTests.DeleteQueuesExecutionListener.class)
public class EnableRabbitIntegrationTests {
@ClassRule
public static final BrokerRunning brokerRunning = BrokerRunning.isRunningWithEmptyQueues(
"test.simple", "test.header", "test.message", "test.reply", "test.sendTo", "test.sendTo.reply",
"test.sendTo.spel", "test.sendTo.reply.spel", "test.sendTo.runtimespel", "test.sendTo.reply.runtimespel",
"test.sendTo.runtimespelsource", "test.sendTo.runtimespelsource.reply",
"test.intercepted", "test.intercepted.withReply",
"test.invalidPojo", "differentTypes", "differentTypes2", "differentTypes3",
"test.inheritance", "test.inheritance.class",
"test.comma.1", "test.comma.2", "test.comma.3", "test.comma.4", "test,with,commas",
"test.converted", "test.converted.list", "test.converted.array", "test.converted.args1",
"test.converted.args2", "test.converted.message", "test.notconverted.message",
"test.notconverted.channel", "test.notconverted.messagechannel", "test.notconverted.messagingmessage",
"test.converted.foomessage", "test.notconverted.messagingmessagenotgeneric", "test.simple.direct",
"test.simple.direct2",
"amqp656dlq", "test.simple.declare", "test.return.exceptions", "test.pojo.errors", "test.pojo.errors2");
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private RabbitTemplate jsonRabbitTemplate;
@Autowired
private RabbitAdmin rabbitAdmin;
@Autowired
private CountDownLatch errorHandlerLatch;
@Autowired
private AtomicReference<Throwable> errorHandlerError;
@Autowired
private String tagPrefix;
@Autowired
private ApplicationContext context;
@Autowired
private TxService txService;
@Autowired
private TxClassLevel txClassLevel;
@Autowired
private MyService service;
@Autowired
private ListenerInterceptor interceptor;
@Autowired
private RabbitListenerEndpointRegistry registry;
@Autowired
private MetaListener metaListener;
@BeforeClass
public static void setUp() {
System.setProperty(RabbitListenerAnnotationBeanPostProcessor.RABBIT_EMPTY_STRING_ARGUMENTS_PROPERTY,
"test-empty");
}
@AfterClass
public static void tearDown() {
System.getProperties().remove(RabbitListenerAnnotationBeanPostProcessor.RABBIT_EMPTY_STRING_ARGUMENTS_PROPERTY);
}
@Test
public void autoDeclare() {
assertEquals("FOO", rabbitTemplate.convertSendAndReceive("auto.exch", "auto.rk", "foo"));
}
@Test
public void autoSimpleDeclare() {
assertEquals("FOOX", rabbitTemplate.convertSendAndReceive("test.simple.declare", "foo"));
}
@Test
public void autoSimpleDeclareAnonymousQueue() {
final SimpleMessageListenerContainer container = (SimpleMessageListenerContainer) registry
.getListenerContainer("anonymousQueue575");
assertThat(container.getQueueNames(), arrayWithSize(1));
assertEquals("viaAnonymous:foo", rabbitTemplate.convertSendAndReceive(container.getQueueNames()[0], "foo"));
}
@Test
public void tx() {
assertTrue(AopUtils.isJdkDynamicProxy(this.txService));
Baz baz = new Baz();
baz.field = "baz";
rabbitTemplate.setReplyTimeout(600000);
assertEquals("BAZ: baz: auto.rk.tx", rabbitTemplate.convertSendAndReceive("auto.exch.tx", "auto.rk.tx", baz));
}
@Test
public void autoDeclareFanout() {
assertEquals("FOOFOO", rabbitTemplate.convertSendAndReceive("auto.exch.fanout", "", "foo"));
}
@Test
public void autoDeclareAnon() {
assertEquals("FOO", rabbitTemplate.convertSendAndReceive("auto.exch", "auto.anon.rk", "foo"));
}
@Test
public void autoStart() {
MessageListenerContainer listenerContainer = this.registry.getListenerContainer("notStarted");
assertNotNull(listenerContainer);
assertFalse(listenerContainer.isRunning());
this.registry.start();
assertTrue(listenerContainer.isRunning());
listenerContainer.stop();
}
@Test
public void autoDeclareAnonWitAtts() {
String received = (String) rabbitTemplate.convertSendAndReceive("auto.exch", "auto.anon.atts.rk", "foo");
assertThat(received, startsWith("foo:"));
org.springframework.amqp.core.Queue anonQueueWithAttributes
= new org.springframework.amqp.core.Queue(received.substring(4), true, true, true);
this.rabbitAdmin.declareQueue(anonQueueWithAttributes); // will fail if atts not correctly set
}
@Test
public void simpleEndpoint() {
assertEquals("FOO", rabbitTemplate.convertSendAndReceive("test.simple", "foo"));
assertEquals(2, this.context.getBean("testGroup", List.class).size());
}
@Test
public void simpleDirectEndpoint() {
String reply = (String) rabbitTemplate.convertSendAndReceive("test.simple.direct", "foo");
assertThat(reply, startsWith("FOOfoo"));
assertThat(reply, containsString("rabbitClientThread-")); // container runs on client thread
assertThat(TestUtils.getPropertyValue(this.registry.getListenerContainer("direct"), "consumersPerQueue"),
equalTo(2));
}
@Test
public void simpleDirectEndpointWithConcurrency() {
String reply = (String) rabbitTemplate.convertSendAndReceive("test.simple.direct2", "foo");
assertThat(reply, startsWith("FOOfoo"));
assertThat(reply, containsString("rabbitClientThread-")); // container runs on client thread
assertThat(TestUtils.getPropertyValue(this.registry.getListenerContainer("directWithConcurrency"),
"consumersPerQueue"), equalTo(3));
}
@Test
public void simpleInheritanceMethod() {
assertEquals("FOO", rabbitTemplate.convertSendAndReceive("test.inheritance", "foo"));
}
@Test
public void simpleInheritanceClass() {
assertEquals("FOOBAR", rabbitTemplate.convertSendAndReceive("test.inheritance.class", "foo"));
}
@Test
public void commas() {
assertEquals("FOOfoo", rabbitTemplate.convertSendAndReceive("test,with,commas", "foo"));
List<?> commaContainers = this.context.getBean("commas", List.class);
assertEquals(1, commaContainers.size());
SimpleMessageListenerContainer container = (SimpleMessageListenerContainer) commaContainers.get(0);
List<String> queueNames = Arrays.asList(container.getQueueNames());
assertThat(queueNames,
contains("test.comma.1", "test.comma.2", "test,with,commas", "test.comma.3", "test.comma.4"));
}
@Test
public void multiListener() {
Bar bar = new Bar();
bar.field = "bar";
rabbitTemplate.convertAndSend("multi.exch", "multi.rk", bar);
rabbitTemplate.setReceiveTimeout(10000);
assertEquals("BAR: bar", this.rabbitTemplate.receiveAndConvert("sendTo.replies"));
Baz baz = new Baz();
baz.field = "baz";
assertEquals("BAZ: baz", rabbitTemplate.convertSendAndReceive("multi.exch", "multi.rk", baz));
Qux qux = new Qux();
qux.field = "qux";
assertEquals("QUX: qux: multi.rk", rabbitTemplate.convertSendAndReceive("multi.exch", "multi.rk", qux));
assertEquals("BAR: barbar", rabbitTemplate.convertSendAndReceive("multi.exch.tx", "multi.rk.tx", bar));
assertEquals("BAZ: bazbaz: multi.rk.tx",
rabbitTemplate.convertSendAndReceive("multi.exch.tx", "multi.rk.tx", baz));
assertTrue(AopUtils.isJdkDynamicProxy(this.txClassLevel));
}
@Test
public void multiListenerJson() {
Bar bar = new Bar();
bar.field = "bar";
String exchange = "multi.json.exch";
String routingKey = "multi.json.rk";
assertEquals("BAR: barMultiListenerJsonBean",
this.jsonRabbitTemplate.convertSendAndReceive(exchange, routingKey, bar));
Baz baz = new Baz();
baz.field = "baz";
assertEquals("BAZ: baz", this.jsonRabbitTemplate.convertSendAndReceive(exchange, routingKey, baz));
Qux qux = new Qux();
qux.field = "qux";
assertEquals("QUX: qux: multi.json.rk",
this.jsonRabbitTemplate.convertSendAndReceive(exchange, routingKey, qux));
// SpEL replyTo
this.jsonRabbitTemplate.convertAndSend(exchange, routingKey, bar);
this.jsonRabbitTemplate.setReceiveTimeout(10000);
assertEquals("BAR: barMultiListenerJsonBean", this.jsonRabbitTemplate.receiveAndConvert("sendTo.replies.spel"));
assertThat(TestUtils.getPropertyValue(this.registry.getListenerContainer("multi"), "concurrentConsumers"),
equalTo(1));
}
@Test
public void endpointWithHeader() {
MessageProperties properties = new MessageProperties();
properties.setHeader("prefix", "prefix-");
Message request = MessageTestUtils.createTextMessage("foo", properties);
Message reply = rabbitTemplate.sendAndReceive("test.header", request);
assertEquals("prefix-FOO", MessageTestUtils.extractText(reply));
}
@Test
public void endpointWithMessage() {
MessageProperties properties = new MessageProperties();
properties.setHeader("prefix", "prefix-");
Message request = MessageTestUtils.createTextMessage("foo", properties);
Message reply = rabbitTemplate.sendAndReceive("test.message", request);
assertEquals("prefix-FOO", MessageTestUtils.extractText(reply));
}
@Test
public void endpointWithComplexReply() {
MessageProperties properties = new MessageProperties();
properties.setHeader("foo", "fooValue");
Message request = MessageTestUtils.createTextMessage("content", properties);
Message reply = rabbitTemplate.sendAndReceive("test.reply", request);
assertEquals("Wrong reply", "content", MessageTestUtils.extractText(reply));
assertEquals("Wrong foo header", "fooValue", reply.getMessageProperties().getHeaders().get("foo"));
assertThat((String) reply.getMessageProperties().getHeaders().get("bar"), startsWith(tagPrefix));
}
@Test
@DirtiesContext
public void simpleEndpointWithSendTo() throws InterruptedException {
rabbitTemplate.convertAndSend("test.sendTo", "bar");
rabbitTemplate.setReceiveTimeout(10000);
Object result = rabbitTemplate.receiveAndConvert("test.sendTo.reply");
assertNotNull(result);
assertEquals("BAR", result);
}
@Test
@DirtiesContext
public void simpleEndpointWithSendToSpel() throws InterruptedException {
rabbitTemplate.convertAndSend("test.sendTo.spel", "bar");
rabbitTemplate.setReceiveTimeout(10000);
Object result = rabbitTemplate.receiveAndConvert("test.sendTo.reply.spel");
assertNotNull(result);
assertEquals("BARbar", result);
}
@Test
public void simpleEndpointWithSendToSpelRuntime() throws InterruptedException {
rabbitTemplate.convertAndSend("test.sendTo.runtimespel", "spel");
rabbitTemplate.setReceiveTimeout(10000);
Object result = rabbitTemplate.receiveAndConvert("test.sendTo.reply.runtimespel");
assertNotNull(result);
assertEquals("runtimespel", result);
}
@Test
public void simpleEndpointWithSendToSpelRuntimeMessagingMessage() throws InterruptedException {
rabbitTemplate.convertAndSend("test.sendTo.runtimespelsource", "spel");
rabbitTemplate.setReceiveTimeout(10000);
Object result = rabbitTemplate.receiveAndConvert("test.sendTo.runtimespelsource.reply");
assertNotNull(result);
assertEquals("sourceEval", result);
}
@Test
public void testInvalidPojoConversion() throws InterruptedException {
this.rabbitTemplate.convertAndSend("test.invalidPojo", "bar");
assertTrue(this.errorHandlerLatch.await(10, TimeUnit.SECONDS));
Throwable throwable = this.errorHandlerError.get();
assertNotNull(throwable);
assertThat(throwable, instanceOf(AmqpRejectAndDontRequeueException.class));
assertThat(throwable.getCause(), instanceOf(ListenerExecutionFailedException.class));
assertThat(throwable.getCause().getCause(),
instanceOf(org.springframework.messaging.converter.MessageConversionException.class));
assertThat(throwable.getCause().getCause().getMessage(),
containsString("Failed to convert message payload 'bar' to 'java.util.Date'"));
}
@Test
public void testDifferentTypes() throws InterruptedException {
Foo1 foo = new Foo1();
foo.setBar("bar");
this.jsonRabbitTemplate.convertAndSend("differentTypes", foo);
assertTrue(this.service.latch.await(10, TimeUnit.SECONDS));
assertThat(this.service.foos.get(0), instanceOf(Foo2.class));
assertEquals("bar", ((Foo2) this.service.foos.get(0)).getBar());
assertThat(TestUtils.getPropertyValue(this.registry.getListenerContainer("different"), "concurrentConsumers"),
equalTo(2));
}
@Test
public void testDifferentTypesWithConcurrency() throws InterruptedException {
Foo1 foo = new Foo1();
foo.setBar("bar");
this.jsonRabbitTemplate.convertAndSend("differentTypes", foo);
assertTrue(this.service.latch.await(10, TimeUnit.SECONDS));
assertThat(this.service.foos.get(0), instanceOf(Foo2.class));
assertEquals("bar", ((Foo2) this.service.foos.get(0)).getBar());
MessageListenerContainer container = this.registry.getListenerContainer("differentWithConcurrency");
assertThat(TestUtils.getPropertyValue(container, "concurrentConsumers"), equalTo(3));
assertNull(TestUtils.getPropertyValue(container, "maxConcurrentConsumers"));
}
@Test
public void testDifferentTypesWithVariableConcurrency() throws InterruptedException {
Foo1 foo = new Foo1();
foo.setBar("bar");
this.jsonRabbitTemplate.convertAndSend("differentTypes", foo);
assertTrue(this.service.latch.await(10, TimeUnit.SECONDS));
assertThat(this.service.foos.get(0), instanceOf(Foo2.class));
assertEquals("bar", ((Foo2) this.service.foos.get(0)).getBar());
MessageListenerContainer container = this.registry.getListenerContainer("differentWithVariableConcurrency");
assertThat(TestUtils.getPropertyValue(container, "concurrentConsumers"), equalTo(3));
assertThat(TestUtils.getPropertyValue(container, "maxConcurrentConsumers"), equalTo(4));
}
@Test
public void testInterceptor() throws InterruptedException {
this.rabbitTemplate.convertAndSend("test.intercepted", "intercept this");
assertTrue(this.interceptor.oneWayLatch.await(10, TimeUnit.SECONDS));
assertEquals("INTERCEPT THIS",
this.rabbitTemplate.convertSendAndReceive("test.intercepted.withReply", "intercept this"));
assertTrue(this.interceptor.twoWayLatch.await(10, TimeUnit.SECONDS));
}
@Test
public void testConverted() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
EnableRabbitConfigWithCustomConversion.class);
RabbitTemplate template = ctx.getBean(RabbitTemplate.class);
Foo1 foo1 = new Foo1();
foo1.setBar("bar");
Jackson2JsonMessageConverter converter = ctx.getBean(Jackson2JsonMessageConverter.class);
converter.setTypePrecedence(TypePrecedence.TYPE_ID);
Object returned = template.convertSendAndReceive("test.converted", foo1);
assertThat(returned, instanceOf(Foo2.class));
assertEquals("bar", ((Foo2) returned).getBar());
assertTrue(TestUtils.getPropertyValue(ctx.getBean("foo1To2Converter"), "converted", Boolean.class));
converter.setTypePrecedence(TypePrecedence.INFERRED);
// No type info in message
template.setMessageConverter(new SimpleMessageConverter());
@SuppressWarnings("resource")
MessagePostProcessor messagePostProcessor = message -> {
message.getMessageProperties().setContentType("application/json");
message.getMessageProperties().setUserId("guest");
return message;
};
returned = template.convertSendAndReceive("", "test.converted", "{ \"bar\" : \"baz\" }", messagePostProcessor);
assertThat(returned, instanceOf(byte[].class));
assertEquals("{\"bar\":\"baz\"}", new String((byte[]) returned));
returned = template.convertSendAndReceive("", "test.converted.list", "[ { \"bar\" : \"baz\" } ]",
messagePostProcessor);
assertThat(returned, instanceOf(byte[].class));
assertEquals("{\"bar\":\"BAZZZZ\"}", new String((byte[]) returned));
returned = template.convertSendAndReceive("", "test.converted.array", "[ { \"bar\" : \"baz\" } ]",
messagePostProcessor);
assertThat(returned, instanceOf(byte[].class));
assertEquals("{\"bar\":\"BAZZxx\"}", new String((byte[]) returned));
returned = template.convertSendAndReceive("", "test.converted.args1", "{ \"bar\" : \"baz\" }",
messagePostProcessor);
assertThat(returned, instanceOf(byte[].class));
assertEquals("\"bar=baztest.converted.args1\"", new String((byte[]) returned));
returned = template.convertSendAndReceive("", "test.converted.args2", "{ \"bar\" : \"baz\" }",
messagePostProcessor);
assertThat(returned, instanceOf(byte[].class));
assertEquals("\"bar=baztest.converted.args2\"", new String((byte[]) returned));
returned = template.convertSendAndReceive("", "test.converted.message", "{ \"bar\" : \"baz\" }",
messagePostProcessor);
assertThat(returned, instanceOf(byte[].class));
assertEquals("\"bar=bazfoo2MessageFoo2Service\"", new String((byte[]) returned));
returned = template.convertSendAndReceive("", "test.notconverted.message", "{ \"bar\" : \"baz\" }",
messagePostProcessor);
assertThat(returned, instanceOf(byte[].class));
assertEquals("\"fooMessage\"", new String((byte[]) returned));
returned = template.convertSendAndReceive("", "test.notconverted.channel", "{ \"bar\" : \"baz\" }",
messagePostProcessor);
assertThat(returned, instanceOf(byte[].class));
assertEquals("\"barAndChannel\"", new String((byte[]) returned));
returned = template.convertSendAndReceive("", "test.notconverted.messagechannel", "{ \"bar\" : \"baz\" }",
messagePostProcessor);
assertThat(returned, instanceOf(byte[].class));
assertEquals("\"bar=bazMessageAndChannel\"", new String((byte[]) returned));
returned = template.convertSendAndReceive("", "test.notconverted.messagingmessage", "{ \"bar\" : \"baz\" }",
messagePostProcessor);
assertThat(returned, instanceOf(byte[].class));
assertEquals("\"GenericMessageLinkedHashMap\"", new String((byte[]) returned));
returned = template.convertSendAndReceive("", "test.converted.foomessage", "{ \"bar\" : \"baz\" }",
messagePostProcessor);
assertThat(returned, instanceOf(byte[].class));
assertEquals("\"GenericMessageFoo2guest\"", new String((byte[]) returned));
returned = template.convertSendAndReceive("", "test.notconverted.messagingmessagenotgeneric",
"{ \"bar\" : \"baz\" }", messagePostProcessor);
assertThat(returned, instanceOf(byte[].class));
assertEquals("\"GenericMessageLinkedHashMap\"", new String((byte[]) returned));
Jackson2JsonMessageConverter jsonConverter = ctx.getBean(Jackson2JsonMessageConverter.class);
DefaultJackson2JavaTypeMapper mapper = TestUtils.getPropertyValue(jsonConverter, "javaTypeMapper",
DefaultJackson2JavaTypeMapper.class);
Mockito.verify(mapper).setBeanClassLoader(ctx.getClassLoader());
ctx.close();
}
@Test
public void testMeta() throws Exception {
rabbitTemplate.convertSendAndReceive("test.metaFanout", "", "foo");
assertTrue(this.metaListener.latch.await(10, TimeUnit.SECONDS));
}
@Test
public void testHeadersExchange() throws Exception {
assertEquals("FOO", rabbitTemplate.convertSendAndReceive("auto.headers", "", "foo",
message -> {
message.getMessageProperties().getHeaders().put("foo", "bar");
message.getMessageProperties().getHeaders().put("baz", "qux");
return message;
}));
assertEquals("BAR", rabbitTemplate.convertSendAndReceive("auto.headers", "", "bar",
message -> {
message.getMessageProperties().getHeaders().put("baz", "fiz");
return message;
}));
}
@Test
public void deadLetterOnDefaultExchange() {
this.rabbitTemplate.convertAndSend("amqp656", "foo");
assertEquals("foo", this.rabbitTemplate.receiveAndConvert("amqp656dlq", 10000));
try {
RabbitManagementTemplate rmt = new RabbitManagementTemplate();
org.springframework.amqp.core.Queue amqp656 = rmt.getQueue("amqp656");
if (amqp656 != null) {
assertEquals("", amqp656.getArguments().get("test-empty"));
assertEquals("undefined", amqp656.getArguments().get("test-null"));
}
}
catch (Exception e) {
// empty
}
}
@Test
@DirtiesContext
public void returnExceptionWithRethrowAdapter() {
this.rabbitTemplate.setMessageConverter(new RemoteInvocationAwareMessageConverterAdapter());
try {
this.rabbitTemplate.convertSendAndReceive("test.return.exceptions", "foo");
fail("ExpectedException");
}
catch (Exception e) {
assertThat(e.getCause().getMessage(), equalTo("return this"));
}
}
@Test
public void listenerErrorHandler() {
assertEquals("BAR", this.rabbitTemplate.convertSendAndReceive("test.pojo.errors", "foo"));
}
@Test
@DirtiesContext
public void listenerErrorHandlerException() {
this.rabbitTemplate.setMessageConverter(new RemoteInvocationAwareMessageConverterAdapter());
try {
this.rabbitTemplate.convertSendAndReceive("test.pojo.errors2", "foo");
fail("ExpectedException");
}
catch (Exception e) {
assertThat(e.getCause().getMessage(), equalTo("from error handler"));
assertThat(e.getCause().getCause().getMessage(), equalTo("return this"));
}
}
@Test
public void testPrototypeCache() {
RabbitListenerAnnotationBeanPostProcessor bpp =
this.context.getBean(RabbitListenerAnnotationBeanPostProcessor.class);
@SuppressWarnings("unchecked")
Map<Class<?>, ?> typeCache = TestUtils.getPropertyValue(bpp, "typeCache", Map.class);
assertFalse(typeCache.containsKey(Foo1.class));
this.context.getBean("foo1Prototype");
assertTrue(typeCache.containsKey(Foo1.class));
Object value = typeCache.get(Foo1.class);
this.context.getBean("foo1Prototype");
assertTrue(typeCache.containsKey(Foo1.class));
assertSame(value, typeCache.get(Foo1.class));
}
interface TxService {
@Transactional
String baz(@Payload Baz baz, @Header("amqp_receivedRoutingKey") String rk);
}
static class TxServiceImpl implements TxService {
@Override
@RabbitListener(bindings = @QueueBinding(
value = @Queue,
exchange = @Exchange(value = "auto.exch.tx", autoDelete = "true"),
key = "auto.rk.tx")
)
public String baz(Baz baz, String rk) {
return "BAZ: " + baz.field + ": " + rk;
}
}
public interface MyServiceInterface {
@RabbitListener(queues = "test.inheritance")
String testAnnotationInheritance(String foo);
}
public static class MyServiceInterfaceImpl implements MyServiceInterface {
@Override
public String testAnnotationInheritance(String foo) {
return foo.toUpperCase();
}
}
@RabbitListener(queues = "test.inheritance.class")
public interface MyServiceInterface2 {
@RabbitHandler
String testAnnotationInheritance(String foo);
}
public static class MyServiceInterfaceImpl2 implements MyServiceInterface2 {
@Override
public String testAnnotationInheritance(String foo) {
return foo.toUpperCase() + "BAR";
}
}
public static class MyService {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "auto.declare", autoDelete = "true"),
exchange = @Exchange(value = "auto.exch", autoDelete = "true"),
key = "auto.rk")
)
public String handleWithDeclare(String foo) {
return foo.toUpperCase();
}
@RabbitListener(queuesToDeclare = @Queue(name = "${jjjj:test.simple.declare}", durable = "true"))
public String handleWithSimpleDeclare(String foo) {
return foo.toUpperCase() + "X";
}
@RabbitListener(queuesToDeclare = @Queue, id = "anonymousQueue575")
public String handleWithAnonymousQueueToDeclare(String data) throws Exception {
return "viaAnonymous:" + data;
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "auto.declare.fanout", autoDelete = "true"),
exchange = @Exchange(value = "auto.exch.fanout", autoDelete = "true", type = "fanout"))
)
public String handleWithFanout(String foo) {
return foo.toUpperCase() + foo.toUpperCase();
}
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,
exchange = @Exchange(value = "auto.exch", autoDelete = "true"),
key = "auto.anon.rk")}
)
public String handleWithDeclareAnon(String foo) {
return foo.toUpperCase();
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(autoDelete = "true", exclusive = "true", durable = "true"),
exchange = @Exchange(value = "auto.exch", autoDelete = "true"),
key = "auto.anon.atts.rk")
)
public String handleWithDeclareAnonQueueWithAtts(String foo, @Header(AmqpHeaders.CONSUMER_QUEUE) String queue) {
return foo + ":" + queue;
}
@RabbitListener(queues = "test.simple", group = "testGroup")
public String capitalize(String foo) {
return foo.toUpperCase();
}
@RabbitListener(id = "direct", queues = "test.simple.direct",
containerFactory = "directListenerContainerFactory")
public String capitalizeDirect1(String foo) {
return foo.toUpperCase() + foo + Thread.currentThread().getName();
}
@RabbitListener(id = "directWithConcurrency", queues = "test.simple.direct2", concurrency = "${ffffx:3}",
containerFactory = "directListenerContainerFactory")
public String capitalizeDirect2(String foo) {
return foo.toUpperCase() + foo + Thread.currentThread().getName();
}
@RabbitListener(queues = {"#{'test.comma.1,test.comma.2'.split(',')}",
"test,with,commas",
"#{commaQueues}"},
group = "commas")
public String multiQueuesConfig(String foo) {
return foo.toUpperCase() + foo;
}
@RabbitListener(queues = "test.header", group = "testGroup")
public String capitalizeWithHeader(@Payload String content, @Header String prefix) {
return prefix + content.toUpperCase();
}
@RabbitListener(queues = "test.message")
public String capitalizeWithMessage(org.springframework.messaging.Message<String> message) {
return message.getHeaders().get("prefix") + message.getPayload().toUpperCase();
}
@RabbitListener(queues = "test.reply")
public org.springframework.messaging.Message<?> reply(String payload, @Header String foo,
@Header(AmqpHeaders.CONSUMER_TAG) String tag) {
return MessageBuilder.withPayload(payload)
.setHeader("foo", foo).setHeader("bar", tag).build();
}
@RabbitListener(queues = "test.sendTo")
@SendTo("test.sendTo.reply")
public String capitalizeAndSendTo(String foo) {
return foo.toUpperCase();
}
@RabbitListener(queues = "test.sendTo.spel")
@SendTo("#{spelReplyTo}")
public String capitalizeAndSendToSpel(String foo) {
return foo.toUpperCase() + foo;
}
@RabbitListener(queues = "test.sendTo.runtimespel")
@SendTo("!{'test.sendTo.reply.' + result}")
public String capitalizeAndSendToSpelRuntime(String foo) {
return "runtime" + foo;
}
@RabbitListener(queues = "test.sendTo.runtimespelsource")
@SendTo("!{source.headers['amqp_consumerQueue'] + '.reply'}")
public String capitalizeAndSendToSpelRuntimeSource(String foo) {
return "sourceEval";
}
@RabbitListener(queues = "test.invalidPojo")
public void handleIt(Date body) {
}
private final List<Object> foos = new ArrayList<Object>();
private final CountDownLatch latch = new CountDownLatch(1);
@RabbitListener(id = "different", queues = "differentTypes", containerFactory = "jsonListenerContainerFactory")
public void handleDifferent(Foo2 foo) {
foos.add(foo);
latch.countDown();
}
@RabbitListener(id = "differentWithConcurrency", queues = "differentTypes2",
containerFactory = "jsonListenerContainerFactory", concurrency = "#{3}")
public void handleDifferentWithConcurrency(Foo2 foo) {
foos.add(foo);
latch.countDown();
}
@RabbitListener(id = "differentWithVariableConcurrency", queues = "differentTypes3",
containerFactory = "jsonListenerContainerFactory", concurrency = "3-4")
public void handleDifferentWithVariableConcurrency(Foo2 foo) {
foos.add(foo);
latch.countDown();
}
@RabbitListener(id = "notStarted", containerFactory = "rabbitAutoStartFalseListenerContainerFactory",
bindings = @QueueBinding(
value = @Queue(autoDelete = "true", exclusive = "true", durable = "true"),
exchange = @Exchange(value = "auto.start", autoDelete = "true", delayed = "${no.prop:false}"),
key = "auto.start")
)
public void handleWithAutoStartFalse(String foo) {
}
@RabbitListener(id = "headersId", bindings = {
@QueueBinding(
value = @Queue(name = "auto.headers1", autoDelete = "true",
arguments = @Argument(name = "x-message-ttl", value = "10000",
type = "java.lang.Integer")),
exchange = @Exchange(name = "auto.headers", type = ExchangeTypes.HEADERS, autoDelete = "true"),
arguments = {
@Argument(name = "x-match", value = "all"),
@Argument(name = "foo", value = "bar"),
@Argument(name = "baz")
}
),
@QueueBinding(
value = @Queue(value = "auto.headers2", autoDelete = "true",
arguments = @Argument(name = "x-message-ttl", value = "10000",
type = "#{T(java.lang.Integer)}")),
exchange = @Exchange(value = "auto.headers", type = ExchangeTypes.HEADERS, autoDelete = "true"),
arguments = {
@Argument(name = "x-match", value = "any"),
@Argument(name = "foo", value = "bax"),
@Argument(name = "#{'baz'}", value = "#{'fiz'}")
}
)
}
)
public String handleWithHeadersExchange(String foo) {
return foo.toUpperCase();
}
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,
exchange = @Exchange(value = "auto.internal", autoDelete = "true", internal = "true"),
key = "auto.internal.rk")}
)
public String handleWithInternalExchange(String foo) {
return foo.toUpperCase();
}
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,
exchange = @Exchange(value = "auto.internal", autoDelete = "true",
ignoreDeclarationExceptions = "true"),
key = "auto.internal.rk")}
)
public String handleWithInternalExchangeIgnore(String foo) {
return foo.toUpperCase();
}
@RabbitListener(id = "defaultDLX",
bindings = @QueueBinding(
value = @Queue(value = "amqp656",
autoDelete = "true",
arguments = {
@Argument(name = "x-dead-letter-exchange", value = ""),
@Argument(name = "x-dead-letter-routing-key", value = "amqp656dlq"),
@Argument(name = "test-empty", value = ""),
@Argument(name = "test-null", value = "") }),
exchange = @Exchange(value = "amq.topic", type = "topic"),
key = "foo"))
public String handleWithDeadLetterDefaultExchange(String foo) {
throw new AmqpRejectAndDontRequeueException("dlq");
}
@RabbitListener(queues = "test.return.exceptions", returnExceptions = "${some.prop:true}")
public String alwaysFails(String data) throws Exception {
throw new Exception("return this");
}
@RabbitListener(queues = "test.pojo.errors", errorHandler = "alwaysBARHandler")
public String alwaysFailsWithErrorHandler(String data) throws Exception {
throw new Exception("return this");
}
@RabbitListener(queues = "test.pojo.errors2", errorHandler = "throwANewException", returnExceptions = "true")
public String alwaysFailsWithErrorHandlerThrowAnother(String data) throws Exception {
throw new Exception("return this");
}
}
public static class Foo1 {
private String bar;
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
public static class Foo2 {
private String bar;
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
@Override
public String toString() {
return "bar=" + this.bar;
}
}
public static class ProxiedListener {
@RabbitListener(queues = "test.intercepted")
public void listen(String foo) {
}
@RabbitListener(queues = "test.intercepted.withReply")
public String listenAndReply(String foo) {
return foo.toUpperCase();
}
}
public static class ListenerInterceptor implements MethodInterceptor {
private final CountDownLatch oneWayLatch = new CountDownLatch(1);
private final CountDownLatch twoWayLatch = new CountDownLatch(1);
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
String methodName = invocation.getMethod().getName();
if (methodName.equals("listen") && invocation.getArguments().length == 1 &&
invocation.getArguments()[0].equals("intercept this")) {
this.oneWayLatch.countDown();
return invocation.proceed();
}
else if (methodName.equals("listenAndReply") && invocation.getArguments().length == 1 &&
invocation.getArguments()[0].equals("intercept this")) {
Object result = invocation.proceed();
if (result.equals("INTERCEPT THIS")) {
this.twoWayLatch.countDown();
}
return result;
}
return invocation.proceed();
}
}
public static class ProxyListenerBPP implements BeanPostProcessor, BeanFactoryAware, Ordered, PriorityOrdered {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ProxiedListener) {
ProxyFactoryBean pfb = new ProxyFactoryBean();
pfb.setProxyTargetClass(true); // CGLIB, false for JDK proxy (interface needed)
pfb.setTarget(bean);
pfb.addAdvice(this.beanFactory.getBean("wasCalled", Advice.class));
return pfb.getObject();
}
else {
return bean;
}
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 1000; // Just before @RabbitListener post processor
}
}
@Configuration
@EnableRabbit
@EnableTransactionManagement
public static class EnableRabbitConfig {
private int increment;
@Bean
public static ProxyListenerBPP listenerProxier() { // note static
return new ProxyListenerBPP();
}
@Bean
public ProxiedListener proxy() {
return new ProxiedListener();
}
@Bean
public static Advice wasCalled() {
return new ListenerInterceptor();
}
@Bean
public String spelReplyTo() {
return "test.sendTo.reply.spel";
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Foo1 foo1Prototype() {
return new Foo1();
}
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(rabbitConnectionFactory());
factory.setErrorHandler(errorHandler());
factory.setConsumerTagStrategy(consumerTagStrategy());
factory.setReceiveTimeout(10L);
return factory;
}
@Bean
public SimpleRabbitListenerContainerFactory rabbitAutoStartFalseListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(rabbitConnectionFactory());
factory.setReceiveTimeout(10L);
factory.setAutoStartup(false);
return factory;
}
@Bean
public SimpleRabbitListenerContainerFactory jsonListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(rabbitConnectionFactory());
factory.setErrorHandler(errorHandler());
factory.setConsumerTagStrategy(consumerTagStrategy());
Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter();
DefaultClassMapper classMapper = new DefaultClassMapper();
Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>();
idClassMapping.put(
"org.springframework.amqp.rabbit.annotation.EnableRabbitIntegrationTests$Foo1", Foo2.class);
classMapper.setIdClassMapping(idClassMapping);
messageConverter.setClassMapper(classMapper);
factory.setMessageConverter(messageConverter);
factory.setReceiveTimeout(10L);
factory.setConcurrentConsumers(2);
return factory;
}
@Bean
public SimpleRabbitListenerContainerFactory simpleJsonListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(rabbitConnectionFactory());
factory.setErrorHandler(errorHandler());
factory.setConsumerTagStrategy(consumerTagStrategy());
factory.setMessageConverter(new Jackson2JsonMessageConverter());
factory.setReceiveTimeout(10L);
return factory;
}
@Bean
public DirectRabbitListenerContainerFactory directListenerContainerFactory() {
DirectRabbitListenerContainerFactory factory = new DirectRabbitListenerContainerFactory();
factory.setConnectionFactory(rabbitConnectionFactory());
factory.setErrorHandler(errorHandler());
factory.setConsumerTagStrategy(consumerTagStrategy());
factory.setConsumersPerQueue(2);
return factory;
}
@Bean
public String tagPrefix() {
return UUID.randomUUID().toString();
}
@Bean
public Collection<org.springframework.amqp.core.Queue> commaQueues() {
org.springframework.amqp.core.Queue comma3 = new org.springframework.amqp.core.Queue("test.comma.3");
org.springframework.amqp.core.Queue comma4 = new org.springframework.amqp.core.Queue("test.comma.4");
List<org.springframework.amqp.core.Queue> list = new ArrayList<org.springframework.amqp.core.Queue>();
list.add(comma3);
list.add(comma4);
return list;
}
@Bean
public ConsumerTagStrategy consumerTagStrategy() {
return queue -> tagPrefix() + this.increment++;
}
@Bean
public CountDownLatch errorHandlerLatch() {
return new CountDownLatch(1);
}
@Bean
public AtomicReference<Throwable> errorHandlerError() {
return new AtomicReference<Throwable>();
}
@Bean
public ErrorHandler errorHandler() {
ErrorHandler handler = Mockito.spy(new ConditionalRejectingErrorHandler());
doAnswer(invocation -> {
try {
return invocation.callRealMethod();
}
catch (Throwable e) {
errorHandlerError().set(e);
errorHandlerLatch().countDown();
throw e;
}
}).when(handler).handleError(Mockito.any(Throwable.class));
return handler;
}
@Bean
public MyService myService() {
return new MyService();
}
@Bean
public RabbitListenerErrorHandler alwaysBARHandler() {
return (m, sm, e) -> "BAR";
}
@Bean
public RabbitListenerErrorHandler throwANewException() {
return (m, sm, e) -> {
throw new RuntimeException("from error handler", e.getCause());
};
}
@Bean
public MyServiceInterface myInheritanceService() {
return new MyServiceInterfaceImpl();
}
@Bean
public MyServiceInterface2 myInheritanceService2() {
return new MyServiceInterfaceImpl2();
}
@Bean
public TxService txService() {
return new TxServiceImpl();
}
// Rabbit infrastructure setup
@Bean
public ConnectionFactory rabbitConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost(brokerRunning.getHostName());
connectionFactory.setPort(brokerRunning.getPort());
connectionFactory.setUsername(brokerRunning.getUser());
connectionFactory.setPassword(brokerRunning.getPassword());
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("rabbitClientThread-");
executor.afterPropertiesSet();
connectionFactory.setExecutor(executor);
return connectionFactory;
}
@Bean
public RabbitTemplate rabbitTemplate() {
return new RabbitTemplate(rabbitConnectionFactory());
}
@Bean
public RabbitTemplate jsonRabbitTemplate() {
RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory());
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
return rabbitTemplate;
}
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}
@Bean
public MultiListenerBean multiListener() {
return new MultiListenerBean();
}
@Bean
public MultiListenerJsonBean multiListenerJson() {
return new MultiListenerJsonBean();
}
@Bean
public PlatformTransactionManager transactionManager() {
return mock(PlatformTransactionManager.class);
}
@Bean
public TxClassLevel txClassLevel() {
return new TxClassLevelImpl();
}
@Bean
public org.springframework.amqp.core.Queue sendToReplies() {
return new org.springframework.amqp.core.Queue(sendToRepliesBean(), false, false, true);
}
@Bean
public org.springframework.amqp.core.Queue sendToRepliesSpEL() {
return new org.springframework.amqp.core.Queue(sendToRepliesSpELBean(), false, false, true);
}
@Bean
public String sendToRepliesSpELBean() {
return "sendTo.replies.spel";
}
@Bean
public String sendToRepliesBean() {
return "sendTo.replies";
}
@Bean
public MetaListener meta() {
return new MetaListener();
}
@Bean
public DirectExchange internal() {
DirectExchange directExchange = new DirectExchange("auto.internal", true, true);
directExchange.setInternal(true);
return directExchange;
}
}
@RabbitListener(bindings = @QueueBinding
(value = @Queue,
exchange = @Exchange(value = "multi.exch", autoDelete = "true"),
key = "multi.rk"))
static class MultiListenerBean {
@RabbitHandler
@SendTo("#{sendToRepliesBean}")
public String bar(Bar bar) {
return "BAR: " + bar.field;
}
@RabbitHandler
public String baz(Baz baz) {
return "BAZ: " + baz.field;
}
@RabbitHandler
public String qux(@Header("amqp_receivedRoutingKey") String rk, @Payload Qux qux) {
return "QUX: " + qux.field + ": " + rk;
}
}
@RabbitListener(id = "multi", bindings = @QueueBinding
(value = @Queue,
exchange = @Exchange(value = "multi.json.exch", autoDelete = "true"),
key = "multi.json.rk"), containerFactory = "simpleJsonListenerContainerFactory")
static class MultiListenerJsonBean {
@RabbitHandler
@SendTo("!{@sendToRepliesSpELBean}")
public String bar(Bar bar, Message message) {
return "BAR: " + bar.field + message.getMessageProperties().getTargetBean().getClass().getSimpleName();
}
@RabbitHandler
public String baz(Baz baz) {
return "BAZ: " + baz.field;
}
@RabbitHandler
public String qux(@Header("amqp_receivedRoutingKey") String rk, @Payload Qux qux) {
return "QUX: " + qux.field + ": " + rk;
}
}
interface TxClassLevel {
@Transactional
String foo(Bar bar);
@Transactional
String baz(@Payload Baz baz, @Header("amqp_receivedRoutingKey") String rk);
}
@RabbitListener(bindings = @QueueBinding
(value = @Queue,
exchange = @Exchange(value = "multi.exch.tx", autoDelete = "true"),
key = "multi.rk.tx"))
static class TxClassLevelImpl implements TxClassLevel {
@Override
@RabbitHandler
public String foo(Bar bar) {
return "BAR: " + bar.field + bar.field;
}
@Override
@RabbitHandler
public String baz(Baz baz, String rk) {
return "BAZ: " + baz.field + baz.field + ": " + rk;
}
}
@SuppressWarnings("serial")
static class Foo implements Serializable {
public String field;
}
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@RabbitListener(bindings = @QueueBinding(
value = @Queue,
exchange = @Exchange(value = "test.metaFanout", type = ExchangeTypes.FANOUT, autoDelete = "true")))
public @interface MyAnonFanoutListener {
}
public static class MetaListener {
private final CountDownLatch latch = new CountDownLatch(2);
@MyAnonFanoutListener
public void handle1(String foo) {
latch.countDown();
}
@MyAnonFanoutListener
public void handle2(String foo) {
latch.countDown();
}
}
@SuppressWarnings("serial")
static class Bar extends Foo implements Serializable {
}
@SuppressWarnings("serial")
static class Baz extends Foo implements Serializable {
}
@SuppressWarnings("serial")
static class Qux extends Foo implements Serializable {
}
@Configuration
@EnableRabbit
public static class EnableRabbitConfigWithCustomConversion implements RabbitListenerConfigurer {
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(rabbitConnectionFactory());
factory.setMessageConverter(jsonConverter());
factory.setReceiveTimeout(10L);
return factory;
}
// Rabbit infrastructure setup
@Bean
public ConnectionFactory rabbitConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost(brokerRunning.getHostName());
connectionFactory.setPort(brokerRunning.getPort());
connectionFactory.setUsername(brokerRunning.getUser());
connectionFactory.setPassword(brokerRunning.getPassword());
return connectionFactory;
}
@Bean
public RabbitTemplate jsonRabbitTemplate() {
RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory());
rabbitTemplate.setMessageConverter(jsonConverter());
return rabbitTemplate;
}
@Bean
public Jackson2JsonMessageConverter jsonConverter() {
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
DefaultJackson2JavaTypeMapper mapper = Mockito.spy(TestUtils.getPropertyValue(jackson2JsonMessageConverter,
"javaTypeMapper", DefaultJackson2JavaTypeMapper.class));
new DirectFieldAccessor(jackson2JsonMessageConverter).setPropertyValue("javaTypeMapper", mapper);
return jackson2JsonMessageConverter;
}
@Bean
public DefaultMessageHandlerMethodFactory myHandlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setMessageConverter(new GenericMessageConverter(myConversionService()));
return factory;
}
@Bean
public ConversionService myConversionService() {
DefaultConversionService conv = new DefaultConversionService();
conv.addConverter(foo1To2Converter());
return conv;
}
@Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
registrar.setMessageHandlerMethodFactory(myHandlerMethodFactory());
}
@Bean
public Converter<Foo1, Foo2> foo1To2Converter() {
return new Converter<Foo1, Foo2>() {
@SuppressWarnings("unused")
private boolean converted;
@Override
public Foo2 convert(Foo1 foo1) {
Foo2 foo2 = new Foo2();
foo2.setBar(foo1.getBar());
converted = true;
return foo2;
}
};
}
@Bean
public Foo2Service foo2Service() {
return new Foo2Service();
}
}
public static class Foo2Service {
@RabbitListener(queues = "test.converted")
public Foo2 foo2(Foo2 foo2) {
return foo2;
}
@RabbitListener(queues = "test.converted.list")
public Foo2 foo2(List<Foo2> foo2s) {
Foo2 foo2 = foo2s.get(0);
foo2.setBar("BAZZZZ");
return foo2;
}
@RabbitListener(queues = "test.converted.array")
public Foo2 foo2(Foo2[] foo2s) {
Foo2 foo2 = foo2s[0];
foo2.setBar("BAZZxx");
return foo2;
}
@RabbitListener(queues = "test.converted.args1")
public String foo2(Foo2 foo2, @Header("amqp_consumerQueue") String queue) {
return foo2 + queue;
}
@RabbitListener(queues = "test.converted.args2")
public String foo2a(@Payload Foo2 foo2, @Header("amqp_consumerQueue") String queue) {
return foo2 + queue;
}
@RabbitListener(queues = "test.converted.message")
public String foo2Message(@Payload Foo2 foo2, Message message) {
return foo2.toString() + message.getMessageProperties().getTargetMethod().getName()
+ message.getMessageProperties().getTargetBean().getClass().getSimpleName();
}
@RabbitListener(queues = "test.notconverted.message")
public String justMessage(Message message) {
return "foo" + message.getClass().getSimpleName();
}
@RabbitListener(queues = "test.notconverted.channel")
public String justChannel(Channel channel) {
return "barAndChannel";
}
@RabbitListener(queues = "test.notconverted.messagechannel")
public String messageChannel(Foo2 foo2, Message message, Channel channel) {
return foo2 + message.getClass().getSimpleName() + "AndChannel";
}
@RabbitListener(queues = "test.notconverted.messagingmessage")
public String messagingMessage(org.springframework.messaging.Message<?> message) {
return message.getClass().getSimpleName() + message.getPayload().getClass().getSimpleName();
}
@RabbitListener(queues = "test.converted.foomessage")
public String messagingMessage(org.springframework.messaging.Message<Foo2> message,
@Header(value = "", required = false) String h,
@Header(name = AmqpHeaders.RECEIVED_USER_ID) String userId) {
return message.getClass().getSimpleName() + message.getPayload().getClass().getSimpleName() + userId;
}
@RabbitListener(queues = "test.notconverted.messagingmessagenotgeneric")
public String messagingMessage(@SuppressWarnings("rawtypes") org.springframework.messaging.Message message,
@Header(value = "", required = false) Integer h) {
return message.getClass().getSimpleName() + message.getPayload().getClass().getSimpleName();
}
}
/**
* Defer queue deletion until after the context has been stopped by the
* {@link DirtiesContext}.
*
*/
public static class DeleteQueuesExecutionListener extends AbstractTestExecutionListener {
@Override
public void afterTestClass(TestContext testContext) throws Exception {
brokerRunning.removeTestQueues("sendTo.replies", "sendTo.replies.spel");
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
}