spring-framework icon indicating copy to clipboard operation
spring-framework copied to clipboard

@SendTo doesn't use MessageConverter

Open b-dzoba opened this issue 6 years ago • 11 comments

Affects: 5.2.0


I configured MappingJackson2MessageConverter, however the app still fails with MessageConversionException.

When I replace @SendTo with template.convertAndSend(), everything works as expected.

Code to reproduce:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.annotation.JmsListenerConfigurer;
import org.springframework.jms.config.JmsListenerEndpointRegistrar;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;

@SpringBootApplication
@EnableJms
@RequiredArgsConstructor
@Slf4j
public class TestApp implements CommandLineRunner, JmsListenerConfigurer {

  private final JmsMessagingTemplate template;

  @JmsListener(destination = "queue1")
  @SendTo("queue2")
  public Payload handle(Payload payload) {
    log.info("Handling message {}", payload);
    return payload;
  }

  @Bean
  public MessageConverter messageConverter() {
    return new MappingJackson2MessageConverter();
  }

  @Bean
  public DefaultMessageHandlerMethodFactory handlerMethodFactory() {
    DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
    factory.setMessageConverter(messageConverter());
    return factory;
  }

  @Override
  public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
    registrar.setMessageHandlerMethodFactory(handlerMethodFactory());
  }

  @Override
  public void run(String... args) {
    template.setMessageConverter(messageConverter());
    template.convertAndSend("queue1", new Payload("test"));
  }

  public static void main(String[] args) {
    SpringApplication.run(TestApp.class, args);
  }

  @Data
  @AllArgsConstructor
  @NoArgsConstructor
  private static class Payload {

    private String value;
  }
}

b-dzoba avatar Oct 29 '19 12:10 b-dzoba

@b-dzoba, did this work for you on a previous version of the Spring Framework, and if so which version?

sbrannen avatar Oct 29 '19 13:10 sbrannen

@sbrannen I haven't tried with different versions. Is there something wrong with my configuration?

5.1.2. doesn't work too.

b-dzoba avatar Oct 29 '19 13:10 b-dzoba

@sbrannen I haven't tried with different versions.

I was asking about that, because I wanted to know if you were experiencing a regression possibly introduced in 5.2.

5.1.2. doesn't work too.

OK. Thanks for letting us know.

Is there something wrong with my configuration?

I'm not sure off the top of my head. One of us will have to look into it in greater detail to determine that.

sbrannen avatar Oct 29 '19 15:10 sbrannen

Seems like the problem is with MessagingMessageListenerAdapter which doesn't call messageConverter before sending reply. Workaround is to override handleResult() in MessagingMessageListenerAdapter like this

public static class CustomMessagingMessageListenerAdapter extends MessagingMessageListenerAdapter {
    private final MessageConverter messageConverter = new MappingJackson2MessageConverter();
    private final JmsHeaderMapper jmsHeaderMapper = new SimpleJmsHeaderMapper();
    @Override
    protected void handleResult(Object result, javax.jms.Message request, Session session) {
        Message<?> resultMessage;
        if (result instanceof org.springframework.messaging.Message) {
            resultMessage = messageConverter.toMessage(((org.springframework.messaging.Message) result).getPayload(), ((org.springframework.messaging.Message) result).getHeaders());
        } else {
            resultMessage = messageConverter.toMessage(result, jmsHeaderMapper.toHeaders(request));
        }
        super.handleResult(resultMessage, request, session);
    }
}

b-dzoba avatar Oct 30 '19 20:10 b-dzoba

@b-dzoba, I think this is where it should be applying message conversion on the return value. Can you try a breakpoint to see if it has the right converter and if so what actually happens there? The converter should be getting set over here.

rstoyanchev avatar Nov 01 '19 10:11 rstoyanchev

@rstoyanchev

Can you try a breakpoint to see if it has the right converter

The converter is null, but this is a org.springframework.jms.support.converter.MessageConverter. And I configured a org.springframework.messaging.converter.MessageConverter, because it's required for @JmsListener (DefaultMessageHandlerMethodFactory). Shouldn't @SendTo be able to use the same converter as @JmsListener?

b-dzoba avatar Nov 03 '19 15:11 b-dzoba

@b-dzoba the converter is shared for incoming messages and replies but it's null so that's a sign of an inconsistent setup. Why don't you configure the messageConverter on the JmsListenerContainerFactory as indicated in the documentation?

A Spring Boot application does most of the heavy lifting for you anyway, see the documentation. In short, if you expose a org.springframework.jms.support.converter.MessageConverter bean, it's going to be detected and used automatically.

I don't understand why that DefaultMessageHandlerMethodFactory bean is defined. If the above doesn't help, please share a sample we can actually run (rather than code in text).

snicoll avatar Nov 06 '19 18:11 snicoll

Why don't you configure the messageConverter on the JmsListenerContainerFactory as indicated in the documentation?

@snicoll Because I use org.springframework.messaging.converter.MappingJackson2MessageConverter for conversion and it doesn't implement org.springframework.jms.support.converter.MessageConverter. This configuration works with @JmsListener but stops working when I add @SendTo

org.springframework.jms.support.converter.MessageConverter bean, it's going to be detected and used automatically

yes, but it doesn't detect org.springframework.messaging.converter.MessageConverter bean, so I have to set it manually on the factory

please share a sample we can actually run

https://github.com/b-dzoba/spring-jms-demo/blob/master/src/main/java/com/example/demo/Issue23886.java

b-dzoba avatar Nov 08 '19 12:11 b-dzoba

I bumped into the same issue, is this being taken care of?

mbolis avatar Jun 23 '22 14:06 mbolis

Unfortunately, https://github.com/b-dzoba/spring-jms-demo is no longer accessible.

@b-dzoba or @mbolis would you be able to share a sample we can run ourselves. That would be very helpful.

snicoll avatar Aug 02 '22 07:08 snicoll

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

spring-projects-issues avatar Aug 09 '22 08:08 spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

spring-projects-issues avatar Aug 16 '22 08:08 spring-projects-issues