Reputation: 280
I created my RabbitListener to get messages from RabbitMQ queue.
My rabbitMQ message:
Properties
priority: 0
delivery_mode: 2
headers:
__TypeId__: com.kmb.bank.models.Transfer
content_encoding: UTF-8
content_type: application/json
Payload
356 bytes
Encoding: string
{"userAccountNumber":"1111444422221111","title":"123","recipientName":"123","recipientAccountNumber":"1234123412341234","amount":123.0,"localDateTime":{"nano":526106200,"year":2018,"monthValue":11,"dayOfMonth":29,"hour":20,"minute":43,"second":0,"month":"NOVEMBER","dayOfWeek":"THURSDAY","dayOfYear":333,"chronology":{"id":"ISO","calendarType":"iso8601"}}}
My Listener method:
@Autowired
private Jackson2JsonMessageConverter jackson2JsonMessageConverter;
@RabbitListener(queues = "kolejka")
public void listen(Message message) {
try {
Transfer transfer = (Transfer) jackson2JsonMessageConverter.fromMessage(message);
log.info(transfer);
} catch (Exception e) {
log.debug("Error thrown while listening + " + e.getMessage());
}
}
Bean config: @Bean public ObjectMapper objectMapper() { return new ObjectMapper(); }
@Bean
public Jackson2JsonMessageConverter jackson2JsonMessageConverter() {
return new Jackson2JsonMessageConverter(objectMapper());
}
And Transfer class:
package com.kmb.transactionlogger.models;
@AllArgsConstructor
public class Transfer {
@Getter @Setter
private String userAccountNumber;
@Getter @Setter
private String title;
@Getter @Setter
private String recipientName;
@Getter @Setter
private String recipientAccountNumber;
@Getter @Setter
private double amount;
@Getter @Setter
private LocalDateTime localDateTime;
}
Unfortunately the exception is being thrown while converting from Message to Transfer transfer object.
Caused by: org.springframework.amqp.support.converter.MessageConversionException: failed to resolve class name. Class not found [com.kmb.bank.models.Transfer]
2018-11-29 20:47:01.615 WARN 13688 --- [cTaskExecutor-1] ingErrorHandler$DefaultExceptionStrategy : Fatal message conversion error; message rejected; it will be dropped or routed to a dead letter exchange, if so configured: (Body:'{"userAccountNumber":"1111444422221111","title":"123","recipientName":"123","recipientAccountNumber":"1234123412341234","amount":123.0,"localDateTime":{"nano":599669800,"year":2018,"monthValue":11,"dayOfMonth":29,"hour":20,"minute":47,"second":1,"month":"NOVEMBER","dayOfWeek":"THURSDAY","dayOfYear":333,"chronology":{"id":"ISO","calendarType":"iso8601"}}}' MessageProperties [headers={__TypeId__=com.kmb.bank.models.Transfer}, contentType=application/json, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, redelivered=false, receivedExchange=bank, receivedRoutingKey=, deliveryTag=2, consumerTag=amq.ctag-sLNqW-_WhDWLWJk6MCQcjg, consumerQueue=kolejka])
Whole message log: https://pastebin.com/raw/47Lq7dYD
Upvotes: 4
Views: 11930
Reputation: 1449
You need to set message converter for both producer and consumer. So when producer sends your object, it will auto convert the object to Json and when consumer receives your Json message, it will auto convert the Json message to original object.
// 1. producer converter config
@Bean("Jackson2JsonMessageConverter")
Jackson2JsonMessageConverter jackson2JsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
@Bean
public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory, Jackson2JsonMessageConverter jackson2JsonMessageConverter) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter);
return rabbitTemplate;
}
// 2. consumer converter config
@RabbitListener(queues = RabbitMQConfig.queueName, messageConverter = "Jackson2JsonMessageConverter")
public void receiveMessage(Object message) {
if (message instanceof Object) {
LOG.info("receiveMessage" + message.toString());
}
}
// 3. send message which is an Java Object
rabbitTemplate.convertAndSend("topicExchangeName", "routing.key.test", Object);
POM:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
Full example: gs-messaging-rabbitmq
Upvotes: 0
Reputation: 1143
I had this issue and when i tried using custom message converters, some would throw errors while trying to resolve to the class specified on the header by the spring producer. I ended up converting the object class to a json string using ObjectMapper
class.
One thing i would like to point is that, doing that will send the content to rabbit MQ as a stringified JSON
. So you can receive it on the consumer class as a string, map it to another custom class using objectMapper.read(stringType, mappedToClass)
.
Another advantage i have gotten is that i can directly read it in other consumers written in python
or nodejs
In summary
In springboot when sending,
rabbitTemplate.convertAndSend(exchange, key, objectMapper.writeValueAsString())
.
In springboot consumer when reading to a custom object that does not have to
match the consumer object.
CustomObj obj = objectMapper.read(incomingString, CustomObj.class)
.
A point to note is that, make sure to
ignoreUnknown
fields in the custom object class,
Upvotes: 0
Reputation: 174484
You have to set up the type mapping in the receiving Jackson2JsonMessageConverter
's type mapper to map to a different class.
Generally the sender would map its class to a token, e.g. transfer
and the receiver will map that to its version of Transfer
.
Alternatively, the framework will infer the type from the parameter if you use public void listen(Transfer transfer)
, and if you wire the converter into the listener container factory.
If it's a Spring Boot application, that wiring will happen automatically.
Upvotes: 3