I try to send a message based on a flatten MAP using Spring Boot and AMQP. The message should then be received using @RabbitListener and transfer it back to a MAP.
First I have nested json String and flat it and send it using the following code:
// Flatten the JSON String returned into a map
Map<String,Object> jsonMap = JsonFlattener.flattenAsMap(result);
rabbitTemplate.convertAndSend(ApplicationProperties.rmqExchange, ApplicationProperties.rmqTopic, jsonMap, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().setHeader("amqp_Key1", "wert1");
message.getMessageProperties().setHeader("amqp_Key2", "Wert2");
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
return message;
}
});
So far so good.
On the receiving site I try to use a Listener and convert the message payload back to the Map as it was send before.
The problem ist that I have no idea how to do it. I receive the message with the following code:
@RabbitListener(queues = "temparea")
public void receiveMessage(Message message) {
log.info("Receiving data from RabbitMQ:");
log.info("Message is of type: " + message.getClass().getName());
log.info("Message: " + message.toString());
}
As I mentioned before I have no idea how I can convert the message to my old MAP. The __ TypeId __ of the Message is: com.github.wnameless.json.flattener.JsonifyLinkedHashMap
I would be more than glad if somebody could assist me how I get this message back to an Java Map.
BR
Update after answer from Artem Bilan:
I added the following code to my configuration file:
@Bean
public SimpleRabbitListenerContainerFactory myRabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setMessageConverter(new Jackson2JsonMessageConverter());
factory.setConnectionFactory(connectionFactory());
factory.setMaxConcurrentConsumers(5);
return factory;
}
But still I have no idea how to get the Map out of my message. The new code block does not change anything.
You have to configure Jackson2JsonMessageConverter bean and Spring Boot will pick it up for the SimpleRabbitListenerContainerFactory bean definition which is used to build listener containers for the @RabbitListener methods.
UPDATE
Pay attention to the Spring AMQP JSON Sample.
There is a bean like jsonConverter(). According Spring Boot auto-configuration this bean is injected to the default:
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
Which is really used for the @RabbitListener by default, when the containerFactory attribute is empty.
So, you need just configure that bean and don't need any custom SimpleRabbitListenerContainerFactory. Or if you do that you should specify its bean name in that containerFactory attribute of your @RabbitListener definitions.
Another option to consider is like Jackson2JsonMessageConverter.setTypePrecedence():
/**
* Set the precedence for evaluating type information in message properties.
* When using {@code @RabbitListener} at the method level, the framework attempts
* to determine the target type for payload conversion from the method signature.
* If so, this type is provided in the
* {@link MessageProperties#getInferredArgumentType() inferredArgumentType}
* message property.
* <p> By default, if the type is concrete (not abstract, not an interface), this will
* be used ahead of type information provided in the {@code __TypeId__} and
* associated headers provided by the sender.
* <p> If you wish to force the use of the {@code __TypeId__} and associated headers
* (such as when the actual type is a subclass of the method argument type),
* set the precedence to {@link TypePrecedence#TYPE_ID}.
* @param typePrecedence the precedence.
* @since 1.6
* @see DefaultJackson2JavaTypeMapper#setTypePrecedence(Jackson2JavaTypeMapper.TypePrecedence)
*/
public void setTypePrecedence(Jackson2JavaTypeMapper.TypePrecedence typePrecedence) {
So, if you want still to have a Message as a method argument but get a gain of the JSON conversion based on the __TypeId__ header, you should consider to configure Jackson2JsonMessageConverter to be based on the Jackson2JavaTypeMapper.TypePrecedence.TYPE_ID.