Issue
Context
A Springboot-based application needs to publish messages to rabbitmq in different format/content-type (xml & json). The application has a dependency on org.springframework.boot:spring-boot-starter-amqp
, springboot version is 2.7.2.
The application has a global RabbitTemplate
to communicate with the rabbitmq, and I'm trying to configure its MessageConverter
to support multiple formats: ContentTypeDelegatingMessageConverter seems to be the good option for that.
Configuration class looks like that (simplified version)
@Bean
public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory,
final Jackson2JsonMessageConverter jsonMessageConverter,
final Jackson2XmlMessageConverter xmlMessageConverter) {
final var rabbitTemplate = new RabbitTemplate(connectionFactory);
ContentTypeDelegatingMessageConverter compositeConverter = new ContentTypeDelegatingMessageConverter();
compositeConverter.addDelegate(MessageProperties.CONTENT_TYPE_JSON, jsonMessageConverter);
compositeConverter.addDelegate(MessageProperties.CONTENT_TYPE_XML, xmlMessageConverter);
rabbitTemplate.setMessageConverter(compositeConverter);
return rabbitTemplate;
A custom service facade provides sendMessage
feature to other services in the application, something like that:
@Service
public class RabbitMQService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage(final String exchange, final String routingKey, final Object payload, String contentType, boolean persistent) {
rabbitTemplate.convertAndSend(exchange, routingKey, payload, message -> {
message.getMessageProperties().setContentType(contentType);
message.getMessageProperties().setDeliveryMode(persistent ? MessageDeliveryMode.PERSISTENT : MessageDeliveryMode.NON_PERSISTENT);
return message;
});
}
}
Problem
I would like to use RabbitTemplate.convertAndSend() method to let Spring create the Message
automatically, using the MessageConverter
.
But these convertAndSend
method will not pass the message properties to the MessageConverter
: it will pass a temporary MessageProperties instance:
public void convertAndSend(String exchange, String routingKey, final Object message,
final MessagePostProcessor messagePostProcessor,
@Nullable CorrelationData correlationData) throws AmqpException {
Message messageToSend = convertMessageIfNecessary(message);
messageToSend = messagePostProcessor.postProcessMessage(messageToSend, correlationData,
nullSafeExchange(exchange), nullSafeRoutingKey(routingKey));
send(exchange, routingKey, messageToSend, correlationData);
}
protected Message convertMessageIfNecessary(final Object object) {
if (object instanceof Message) {
return (Message) object;
}
return getRequiredMessageConverter().toMessage(object, new MessageProperties());
}
The ContentTypeDelegatingMessageConverter will therefor always select the default converter as MessageProperties will always have the default ContentType set (octet-stream).
This makes ContentTypeDelegatingMessageConverter
class a bit useless in this case. I don't find any convertAndSend
method that take Message Properties as input and provide it to the converter.
Are there any other ways to make ContentTypeDelegatingMessageConverter
and RabbitTemplate
work nicely together?
Solution
Looks like a ContentTypeDelegatingMessageConverter
was designed for the inbound messages conversion in the MessageListenerContainer
. Where we have the whole Message
context with respective properties and contentType
in there.
On the publish side we indeed don't have an API to accept a body
and content type to convert it to byte[]
. Please, raise a GH issue and we will think what we can do to make it really as it is advertised in the docs even for message publishing.
Meanwhile, as a workaround, I'd suggest to call this converter manually before propagating result Message
to the RabbitTemplate.send()
.
Answered By - Artem Bilan
Answer Checked By - David Marino (JavaFixing Volunteer)