Search code examples
spring-bootspring-amqpspring-rabbit

Spring AMQP - Unable to use ContentTypeDelegatingMessageConverter with RabbitTemplate


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().