Reputation: 31
RabbitMQ messages are arriving with type information:
headers:
__ContentTypeId__: java.lang.Object
__TypeId__: java.util.ArrayList
I have these 2 container factory beans in code - default one uses converter, another not:
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
final SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
final Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter();
factory.setMessageConverter(messageConverter);
return factory;
}
@Bean
public SimpleRabbitListenerContainerFactory rawContainerFactory(ConnectionFactory connectionFactory) {
final SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
return factory;
}
Scenario 1. A listener.
@RabbitListener(queues = "#{@myQueueBean}")
public void persistMessage(Message message) {
.....
}
As I check what arrives into listener, message.payload is ArrayList, with HashMaps inside. This looks ok, seems to be using rabbitListenerContainerFactory as a default.
Scenario 2. I change to use containerFactory without converter:
@RabbitListener(queues = "#{@myQueueBean}", containerFactory = "rawContainerFactory")
public void persistMessage(Message message) {
.....
}
As I check what arrives, message.payload is now byte[] with contents of message. Again looks ok.
Scenario 3. I change to explicitly use the default containerFactory:
@RabbitListener(queues = "#{@myQueueBean}", containerFactory = "rabbitListenerContainerFactory")
public void persistMessage(Message message) {
.....
}
As I check what arrives, message.payload is again byte[] with contents of message. This sounds unexpected, I hoped for ArrayList as in Scenario 1.
Now I comment out messageConverter in first bean:
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
// Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter();
// factory.setMessageConverter(messageConverter);
return factory;
}
@Bean
public SimpleRabbitListenerContainerFactory rawContainerFactory(ConnectionFactory connectionFactory) {
final SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
return factory;
}
Scenario 4. Listener again
@RabbitListener(queues = "#{@myQueueBean}")
public void persistMessage(Message message) {
.....
}
As I check what arrives, I expect message.payload as byte[] (default containerfactory to be used, without converter like Scenario 2), but actually I'm getting a String of comma separated numbers. This sounds wrong.
Scenario 5. I change to explicitly use the default containerFactory without converter:
@RabbitListener(queues = "#{@myQueueBean}", containerFactory = "rabbitListenerContainerFactory")
public void persistMessage(Message message) {
.....
}
As I check what arrives, message.payload is now byte[] with contents of message. This is like Scenario 2 and looks ok.
Scenario 6. Use "rawContainerFactory", result is as now logically expected a byte[] array.
Scenario 7. Use "rawContainerFactory" and add Jackson converter to it. Result is ArrayList. Quite unexpected after Scenario 3 results.
Results above look strongly inconsistent. There is some magic going with the "rabbitListenerContainerFactory" which prompts me to say - forget it and use own custom factories in explicit configuration. Unless someone manages to explain this magic.
Update: I cannot reproduce the issue anymore, and that is yet another magic why it's gone. Both Scenario 3 and Scenario 4 issues are gone, and I literally did nothing except adding one more testcase to send message to queue using RabbitTemplate (but still was using real big jsons from queue for testing). And before and now I was testing with both Eclipse and Idea on the same physical project code on the disk. One additional note, all my listeners in my samples are using non-generic Message type parameter. I did that explicitly because adding any specific type to some of the scenarios (that could be Scenario 1 or 4 if I remember correctly) - even the < ? > was changing it's behavior to the point where message would not even reach my listener and there would be an error in logs saying something "cannot convert START_OBJECT to some-type".
Upvotes: 0
Views: 2848
Reputation: 174729
I can't reproduce the behavior you cite. Everything works as expected for me.
@SpringBootApplication
public class So47393130Application {
public static void main(String[] args) {
SpringApplication.run(So47393130Application.class, args).close();
}
@Bean
public ApplicationRunner runner(RabbitTemplate rabbitTemplate) {
return args -> {
rabbitTemplate.convertAndSend("foo", "", new ArrayList<>(Arrays.asList("foo", "bar")));
Thread.sleep(5_000);
};
}
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
final SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
final Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter();
factory.setMessageConverter(messageConverter);
return factory;
}
@Bean
public SimpleRabbitListenerContainerFactory rawContainerFactory(ConnectionFactory connectionFactory) {
final SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
return factory;
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
return rabbitTemplate;
}
@Bean
public Queue foo() {
return new Queue("foo");
}
@Bean
public Queue bar() {
return new Queue("bar");
}
@Bean
public Queue baz() {
return new Queue("baz");
}
@Bean
public FanoutExchange exchange() {
return new FanoutExchange("foo");
}
@Bean
public Binding fooBinding() {
return BindingBuilder.bind(foo()).to(exchange());
}
@Bean
public Binding barBinding() {
return BindingBuilder.bind(bar()).to(exchange());
}
@Bean
public Binding bazBinding() {
return BindingBuilder.bind(baz()).to(exchange());
}
@RabbitListener(queues = "foo")
public void foo(Message<?> in) {
System.out.println("foo:" + in.getPayload());
}
@RabbitListener(queues = "bar", containerFactory = "rawContainerFactory")
public void bar(Message<?> in) {
System.out.println("bar:" + in.getPayload());
}
@RabbitListener(queues = "baz", containerFactory = "rabbitListenerContainerFactory")
public void baz(Message<?> in) {
System.out.println("baz:" + in.getPayload());
}
}
Perhaps you can set a breakpoint in your listener and look down the stack to see exactly which converter is wired into the listener adapter.
Upvotes: 1