/*
* Copyright 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.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.Level;
import org.junit.AfterClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.AsyncRabbitTemplate;
import org.springframework.amqp.rabbit.AsyncRabbitTemplate.RabbitConverterFuture;
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.RabbitTemplate;
import org.springframework.amqp.rabbit.junit.BrokerRunning;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter;
import org.springframework.amqp.rabbit.test.LogLevelAdjuster;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author Gary Russell
* @since 2.0
*
*/
@RunWith(SpringRunner.class)
@DirtiesContext
public class ComplexTypeJsonIntegrationTests {
private static final String TEST_QUEUE = "test.complex.send.and.receive";
private static final String TEST_QUEUE2 = "test.complex.receive";
@ClassRule
public static BrokerRunning brokerRunning = BrokerRunning.isRunningWithEmptyQueues(TEST_QUEUE, TEST_QUEUE2);
@Rule
public LogLevelAdjuster adjuster = new LogLevelAdjuster(Level.DEBUG, RabbitTemplate.class,
MessagingMessageListenerAdapter.class,
SimpleMessageListenerContainer.class);
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private AsyncRabbitTemplate asyncTemplate;
@AfterClass
public static void tearDown() {
brokerRunning.removeTestQueues();
}
private static Foo<Bar<Baz, Qux>> makeAFoo() {
Foo<Bar<Baz, Qux>> foo = new Foo<>();
Bar<Baz, Qux> bar = new Bar<>();
bar.setaField(new Baz("foo"));
bar.setbField(new Qux(42));
foo.setField(bar);
return foo;
}
/*
* Covers all flavors of convertSendAndReceiveAsType
*/
@Test
public void testSendAndReceive() throws Exception {
verifyFooBarBazQux(this.rabbitTemplate.convertSendAndReceiveAsType("foo",
new ParameterizedTypeReference<Foo<Bar<Baz, Qux>>>() { }));
verifyFooBarBazQux(this.rabbitTemplate.convertSendAndReceiveAsType("foo",
m -> m,
new ParameterizedTypeReference<Foo<Bar<Baz, Qux>>>() { }));
verifyFooBarBazQux(this.rabbitTemplate.convertSendAndReceiveAsType(TEST_QUEUE, "foo",
new ParameterizedTypeReference<Foo<Bar<Baz, Qux>>>() { }));
verifyFooBarBazQux(this.rabbitTemplate.convertSendAndReceiveAsType(TEST_QUEUE, "foo",
m -> m,
new ParameterizedTypeReference<Foo<Bar<Baz, Qux>>>() { }));
verifyFooBarBazQux(this.rabbitTemplate.convertSendAndReceiveAsType("", TEST_QUEUE, "foo",
new ParameterizedTypeReference<Foo<Bar<Baz, Qux>>>() { }));
verifyFooBarBazQux(this.rabbitTemplate.convertSendAndReceiveAsType("", TEST_QUEUE, "foo",
m -> m,
new ParameterizedTypeReference<Foo<Bar<Baz, Qux>>>() { }));
}
@Test
public void testReceive() {
this.rabbitTemplate.convertAndSend(TEST_QUEUE2, makeAFoo(), m -> {
m.getMessageProperties().getHeaders().remove("__TypeId__");
return m;
});
verifyFooBarBazQux(
this.rabbitTemplate.receiveAndConvert(10_000, new ParameterizedTypeReference<Foo<Bar<Baz, Qux>>>() { }));
}
@Test
public void testReceiveNoWait() throws Exception {
this.rabbitTemplate.convertAndSend(TEST_QUEUE2, makeAFoo(), m -> {
m.getMessageProperties().getHeaders().remove("__TypeId__");
return m;
});
Foo<Bar<Baz, Qux>> foo =
this.rabbitTemplate.receiveAndConvert(new ParameterizedTypeReference<Foo<Bar<Baz, Qux>>>() { });
int n = 0;
while (n++ < 100 && foo == null) {
Thread.sleep(100);
foo = this.rabbitTemplate.receiveAndConvert(new ParameterizedTypeReference<Foo<Bar<Baz, Qux>>>() { });
}
verifyFooBarBazQux(foo);
}
@Test
public void testAsyncSendAndReceive() throws Exception {
verifyFooBarBazQux(this.asyncTemplate.convertSendAndReceiveAsType("foo",
new ParameterizedTypeReference<Foo<Bar<Baz, Qux>>>() { }));
verifyFooBarBazQux(this.asyncTemplate.convertSendAndReceiveAsType("foo",
m -> m,
new ParameterizedTypeReference<Foo<Bar<Baz, Qux>>>() { }));
verifyFooBarBazQux(this.asyncTemplate.convertSendAndReceiveAsType(TEST_QUEUE, "foo",
new ParameterizedTypeReference<Foo<Bar<Baz, Qux>>>() { }));
verifyFooBarBazQux(this.asyncTemplate.convertSendAndReceiveAsType(TEST_QUEUE, "foo",
m -> m,
new ParameterizedTypeReference<Foo<Bar<Baz, Qux>>>() { }));
verifyFooBarBazQux(this.asyncTemplate.convertSendAndReceiveAsType("", TEST_QUEUE, "foo",
new ParameterizedTypeReference<Foo<Bar<Baz, Qux>>>() { }));
verifyFooBarBazQux(this.asyncTemplate.convertSendAndReceiveAsType("", TEST_QUEUE, "foo",
m -> m,
new ParameterizedTypeReference<Foo<Bar<Baz, Qux>>>() { }));
}
private void verifyFooBarBazQux(RabbitConverterFuture<Foo<Bar<Baz, Qux>>> future) throws Exception {
verifyFooBarBazQux(future.get(10, TimeUnit.SECONDS));
}
private void verifyFooBarBazQux(Foo<?> foo) {
assertNotNull(foo);
Bar<?, ?> bar;
assertThat(foo.getField(), instanceOf(Bar.class));
bar = (Bar<?, ?>) foo.getField();
assertThat(bar.getaField(), instanceOf(Baz.class));
assertThat(bar.getbField(), instanceOf(Qux.class));
}
@Configuration
@EnableRabbit
public static class ContextConfig {
@Bean
public ConnectionFactory cf() {
return new CachingConnectionFactory("localhost");
}
@Bean
public RabbitTemplate template() {
RabbitTemplate rabbitTemplate = new RabbitTemplate(cf());
rabbitTemplate.setRoutingKey(TEST_QUEUE);
rabbitTemplate.setQueue(TEST_QUEUE2);
rabbitTemplate.setMessageConverter(jsonMessageConverter());
return rabbitTemplate;
}
@Bean
public AsyncRabbitTemplate asyncTemplate() {
return new AsyncRabbitTemplate(template());
}
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(cf());
factory.setMessageConverter(jsonMessageConverter());
return factory;
}
@Bean
public Listener listener() {
return new Listener();
}
}
public static class Listener {
@RabbitListener(queues = TEST_QUEUE)
public Foo<Bar<Baz, Qux>> listen(String in) {
return makeAFoo();
}
}
public static class Foo<T> {
private T field;
public T getField() {
return this.field;
}
public void setField(T field) {
this.field = field;
}
@Override
public String toString() {
return "Foo [field=" + this.field + "]";
}
}
public static class Bar<A, B> {
private A aField;
private B bField;
public A getaField() {
return this.aField;
}
public void setaField(A aField) {
this.aField = aField;
}
public B getbField() {
return this.bField;
}
public void setbField(B bField) {
this.bField = bField;
}
@Override
public String toString() {
return "Bar [aField=" + this.aField + ", bField=" + this.bField + "]";
}
}
public static class Baz {
private String baz;
Baz() {
super();
}
public Baz(String string) {
this.baz = string;
}
public String getBaz() {
return this.baz;
}
public void setBaz(String baz) {
this.baz = baz;
}
@Override
public String toString() {
return "Baz [baz=" + this.baz + "]";
}
}
public static class Qux {
private Integer qux;
Qux() {
super();
}
public Qux(int i) {
this.qux = i;
}
public Integer getQux() {
return this.qux;
}
public void setQux(Integer qux) {
this.qux = qux;
}
@Override
public String toString() {
return "Qux [qux=" + this.qux + "]";
}
}
}