smallrye-reactive-messaging
smallrye-reactive-messaging copied to clipboard
RabbitMQ - print "SRMSG17038: No valid content_type set" when content encode set
I use quarkus-smallrye-reactive-messaging-rabbitmq in my quarkus project
provide:
@Incoming("test-a")
@Outgoing("test-b")
public Message<Price> provide(Message<byte[]> message) {
final OutgoingRabbitMQMetadata metadata = new OutgoingRabbitMQMetadata.Builder()
.withContentEncoding(StandardCharsets.UTF_8.name())
.withContentType("application/json")
.build();
System.out.printf(new String(message.getPayload()));
message.ack();
return Message.of(new Price(new String(message.getPayload()),1.1),
Metadata.of(metadata));
}
consume:
@Incoming("test-b")
public Uni<Void> consume(Message<JsonObject> price) {
Optional<IncomingRabbitMQMetadata> metadata = price.getMetadata(
IncomingRabbitMQMetadata.class);
metadata.ifPresent(meta -> {
final Optional<String> contentEncoding = meta.getContentEncoding();
System.out.println(contentEncoding.orElse("empty content"));
final Optional<String> contentType = meta.getContentType();
System.out.println(contentType.orElse(""));
});
System.out.println(price.getPayload().toString());
price.ack();
return Uni.createFrom().voidItem();
}
log with:
2023-06-29 11:13:49,514 WARN [io.sma.rea.mes.rabbitmq] (vert.x-eventloop-thread-0) SRMSG17038: No valid content_type set, failing back to byte[]. If that's wanted, set the content type to application/octet-stream with "content-type-override"
UTF-8
application/json
2023-06-29 11:13:49,530 ERROR [io.sma.rea.mes.provider] (vert.x-eventloop-thread-0) SRMSG00200: The method RabbitMQPriceConsumer#consume has thrown an exception: java.lang.ClassCastException: class [B cannot be cast to class io.vertx.core.json.JsonObject ([B is in module java.base of loader 'bootstrap'; io.vertx.core.json.JsonObject is in unnamed module of loader io.quarkus.bootstrap.classloading.QuarkusClassLoader @5606c0b)
When I remove this line .withContentEncoding(StandardCharsets.UTF_8.name())
It's right.
2023-06-29 11:34:02,828 INFO [io.sma.rea.mes.rabbitmq] (vert.x-eventloop-thread-0) SRMSG17000: RabbitMQ Receiver listening address user-sync.test-b
empty content
application/json
{"name":"no utf-8","price":1.1}
And if I keep .withContentEncoding(StandardCharsets.UTF_8.name())
and use byte[] in consume
param :
@Incoming("test-b")
public Uni<Void> consume(Message<byte[]> price) {
Optional<IncomingRabbitMQMetadata> metadata = price.getMetadata(
IncomingRabbitMQMetadata.class);
metadata.ifPresent(meta -> {
final Optional<String> contentEncoding = meta.getContentEncoding();
System.out.println(contentEncoding.orElse("empty content"));
final Optional<String> contentType = meta.getContentType();
System.out.println(contentType.orElse(""));
});
System.out.println(new String(price.getPayload()));
price.ack();
return Uni.createFrom().voidItem();
}
the print is right but have a warnging in log :
2023-06-29 11:36:44,890 WARN [io.sma.rea.mes.rabbitmq] (vert.x-eventloop-thread-0) SRMSG17038: No valid content_type set, failing back to byte[]. If that's wanted, set the content type to application/octet-stream with "content-type-override"
UTF-8
application/json
{"name":"use utf-8 and consume param use byte","price":1.1}
If remove .withContentEncoding(StandardCharsets.UTF_8.name())
and use byte[] in consume param is same as use utf-8 use JsonObject into consume param
empty content
application/json
2023-06-29 11:42:16,074 ERROR [io.sma.rea.mes.provider] (vert.x-eventloop-thread-0) SRMSG00200: The method RabbitMQPriceConsumer#consume has thrown an exception: java.lang.ClassCastException: class io.vertx.core.json.JsonObject cannot be cast to class [B (io.vertx.core.json.JsonObject is in unnamed module of loader io.quarkus.bootstrap.classloading.QuarkusClassLoader @21a947fe; [B is in module java.base of loader 'bootstrap')
This has always annoyed me. I'm not sure what the reasoning for this behavior is but...
If you set contentEncoding
, it ignores contentType
, and warns you if contentEncoding
is not application/octet-stream
. Finally it always returns a byte[]
You can silence the warning (as the log states) by setting content-type-override
in the connector configuration.
If you set contentType
it must be application/json
or text/plain
exactly (no media-type arguments like charset
). For application/json
it returns a Vert.x JsonObject
, JsonArray
, String
, Number
or Bool
; the Vert.x JSON types. For text/plain
it returns String
and assumes it's UTF-8 encoded.
So, according to those complicated rules...
In your first example you are receiving a JsonObject
and setting contentEncoding
, this violates the rule that setting contentEncoding
will return a byte[]
, and you get the casting exception.
In your second example, you are receiving a byte[]
and setting contentEncoding
, but you are setting it to UTF-8
; so this works but since as the log states you need to set content-type-override
to application/octet-stream
for the connector configuration. Basically by setting content-type-override
you are stating that even though you've set the contentType
to UTF-8
(something it doesn't understand), you realize you will always get back a byte[]
value.
@ozangunalp The rules governing this are not great. I know there's a chart in the docs but maybe it needs to be admonished with a warning or something; I think this is a common mistake. Also, the need to add content-type-override
always seemed weird to me.
Could there be a way to interrogate the @Incoming
annotated method and make this more friendly. If IncomingRabbitMQMessage.convertValue()
could see the target type it could convert based on that.
It would also need to know about MessageConverter
s as well but those are already passed to ConverterUtils.convert
. IncomingRabbitMQMessage.convertValue()
could check if there's a matching converter and if so, just send the Buffer
. Alternatively, maybe allow annotating the MessageConverter
with a @PayloadType
annotation to ensure the it's providing the right value to the MessageConvert
(but then we are double converting which doesn't seem right).
I think there are options here for a much more developer friendly interface.
I guess I missed the obvious and easy solution (that is also a good developer experience), always just send Buffer
and remove convertValue
. MessageConverter
s exist and are well documented. You get Buffer
use a MessageConverter
to receive anything else.
Who defined these rules? Is that in the connector or in RabbitMQ?
About conversion, I would instead use converters and as you say, always send Buffers in the incoming connector.
The rules were created by whoever created the original RabbitMQ connector code. They are not related to RabbitMQ in any way.
I think it was just a way to do some conversion before conversion existed.