I'm looking for a way to use the same method to receive messages converted by myMessageConverter
to an object depending on __TypeId__
header.
WHY? We are migrating from IBM MQ to RabbitMQ. Currently, it works like that:
@JmsListener(destination = "${jms.topic})
public void receiveMessage(@NonNull Message<Object> message) {
Object payload = message.getPayload();>
processByType(payload);
}
I'd like to have that something like that:
@RabbitListener(queues = "${queue-name}", messageConverter = "myMessageConverter")
public void receiveMessage(@Payload Object payload) {
// use existing logic
processByType(payload);
}
But in the case from above, it passes the object of type Message
with all its details.
I found information that the method should have one parameter of a certain type (e.g.: MyPayload
). and it works.
@RabbitListener(queues = "${queue-name}", messageConverter = "myMessageConverter")
public void receiveMessage(@Payload MyPayload payload) { ... }
It even works for message types inherited from a common interface, e.g., MyInboundPayload.
@RabbitListener(queues = "${queue-name}", messageConverter = "myMessageConverter")
public void receiveMessage(@Payload MyInboundPayload payload) { ... }
And I'd go with this option, but half of the payload types are library classes imported from other services. And I'm unable to make them implement MyInboundPayload
.
So, does anyone know how to force it to pass an already converted message to the method? Is that possible?
public void receiveMessage(@Payload Object payload)
Thanks!
Current configuration:
@Bean
public MessageConverter myMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
.registerModule(new ParanamerModule())
.registerModule(new SingleArgConstructorModule("com.xyz.api"))
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
return new Jackson2JsonMessageConverter(objectMapper);
}
p.s. I know that I can process Message
and then get type and manually convert to a type. But I wonder if there is a way to make with @RabbitListener
capabilities.
I'm looking for a way to use the same method to receive messages converted by myMessageConverter
to an object depending on __TypeId__
header.
WHY? We are migrating from IBM MQ to RabbitMQ. Currently, it works like that:
@JmsListener(destination = "${jms.topic})
public void receiveMessage(@NonNull Message<Object> message) {
Object payload = message.getPayload();>
processByType(payload);
}
I'd like to have that something like that:
@RabbitListener(queues = "${queue-name}", messageConverter = "myMessageConverter")
public void receiveMessage(@Payload Object payload) {
// use existing logic
processByType(payload);
}
But in the case from above, it passes the object of type Message
with all its details.
I found information that the method should have one parameter of a certain type (e.g.: MyPayload
). and it works.
@RabbitListener(queues = "${queue-name}", messageConverter = "myMessageConverter")
public void receiveMessage(@Payload MyPayload payload) { ... }
It even works for message types inherited from a common interface, e.g., MyInboundPayload.
@RabbitListener(queues = "${queue-name}", messageConverter = "myMessageConverter")
public void receiveMessage(@Payload MyInboundPayload payload) { ... }
And I'd go with this option, but half of the payload types are library classes imported from other services. And I'm unable to make them implement MyInboundPayload
.
So, does anyone know how to force it to pass an already converted message to the method? Is that possible?
public void receiveMessage(@Payload Object payload)
Thanks!
Current configuration:
@Bean
public MessageConverter myMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
.registerModule(new ParanamerModule())
.registerModule(new SingleArgConstructorModule("com.xyz.api"))
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
return new Jackson2JsonMessageConverter(objectMapper);
}
p.s. I know that I can process Message
and then get type and manually convert to a type. But I wonder if there is a way to make with @RabbitListener
capabilities.
1 Answer
Reset to default 1Configure your Jackson2JsonMessageConverter
to use the header to determine the type instead of the default to look at the method signature. See the javadoc for more information.
That being said your configuration is almost there, you are just missing the setting of the typePrecedence
property. Default it points to INFERRED
which means look at the method argument, but you can set it to TYPE_ID
to force it to use the header.
@Bean
public MessageConverter myMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
.registerModule(new ParanamerModule())
.registerModule(new SingleArgConstructorModule("com.xyz.api"))
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
var converter = new Jackson2JsonMessageConverter(objectMapper);
converter.setTypePrecedence(TypePrecedence.TYPE_ID);
return converter;
}
This configuration change will make it look at the header before looking at the type. So now you should be able to specify @Payload Object
and get the actual type.
See comment from Artem Bilan about the security restrictions (CVE-2017-4995).
You would need to pass the possible packages to the configuration as well.
var converter = new Jackson2JsonMessageConverter(objectMapper);
converter.setTypePrecedence(TypePrecedence.TYPE_ID);
converter.setTrustedPackages("com.yourpackage.sub1", "com.yourpackage.sub2");
return converter;
If you don't do this you will get an exception from Jackson about the class not being allowed.
myMessageConverter
do compared to the default ones? The default one you can "enforce" to use the__TYPE_ID__
header to do the deserialization, but without knowing what yourmyMessageConverter
is that is going to be impossible to determine if that is feasible. – M. Deinum Commented Mar 31 at 14:01