Reputation: 41
I used to have a @RabbitListener
that works fine like this:
@Component
public class AppointmentMessageListener {
@RabbitListener(queues = "${rabbitmq.immediateQueueName}")
public void receiveImmediateAppointmentMessage(AppointmentMessage appointmentMessage) {
// Do something
}
}
Now I want to have a different type of message on the same queue, and I tried it like the docs said:
@Component
@RabbitListener(queues = "${rabbitmq.immediateQueueName}")
public class AppointmentMessageListener {
@RabbitHandler
public void receiveImmediateAppointmentMessage(AppointmentMessage appointmentMessage) {
// Do something
}
@RabbitHandler
public void receiveImmediateAppointmentMessage(AppointmentDeleteMessage appointmentDeleteMessage) {
// Do something else
}
}
This doesn't work, and I get org.springframework.amqp.AmqpException: No method found for class java.util.LinkedHashMap
.
The JSON looks like below, and the difference between the 2 types is only in the object
structure. I don't control the structure of the message. Is there any way I can detect the correct type to use in my multimethod listener?
{
"key":"event-message-resource.immediate",
"creationTimestamp":1643804135376,
"object": {
// here is the object I am interested in
}
}
I use the following configuration besides the exchange, queue and routing key declarables.:
@Bean
public Jackson2JsonMessageConverter jsonMessageConverter() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
return new Jackson2JsonMessageConverter(objectMapper);
}
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer,
ConnectionFactory connectionFactory,
AuthenticationRabbitListenerAdvice rabbitListenerAdvice) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
configurer.configure(factory, connectionFactory);
factory.setAdviceChain(combineAdvice(factory.getAdviceChain(), rabbitListenerAdvice));
return factory;
}
Upvotes: 0
Views: 1369
Reputation: 41
Based on the answer from the revered Gary Russell and his answer on his post here, I managed to get it to work by subclassing the ClassMapper and deciding on the type based on the routing key.
@Component
@RequiredArgsConstructor
public class CustomClassMapper extends DefaultJackson2JavaTypeMapper {
@Value("${rabbitmq.deleteRoutingKey}")
private final String deleteRoutingKey;
@NonNull
@Override
public Class<?> toClass(MessageProperties properties) {
String routingKey = properties.getReceivedRoutingKey();
if (deleteRoutingKey.equals(routingKey)) {
return AppointmentDeleteMessage.class;
}
return AppointmentMessage.class;
}
}
Then I set the classMapper
in the messageConverter
.
@Bean
public Jackson2JsonMessageConverter jsonMessageConverter(CustomClassMapper customClassMapper) {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter(objectMapper);
messageConverter.setClassMapper(customClassMapper);
return messageConverter;
}
Upvotes: 1
Reputation: 174749
The framework can't infer the target type when using @RabbitHandler
because it uses the type to select the method. The JSON has to be converted before the method selection is performed.
You need to use something in the message to determine which type to create.
Upvotes: 2