Vert.x 4 eventbus serialize multiple classes with same codec

Viewed 164

Is there a way to register a codec for multiple classes? Basically, all my classes should just be serialized using a Jackson object mapper. But it seems like I have to create a custom codec for each class (even though I can abstract it a little bit using generics).

A small code example:

Codec:

class JacksonCodec<T>(private val mapper: ObjectMapper, private val clazz: Class<T>) : MessageCodec<T, T> {
    override fun encodeToWire(buffer: Buffer, s: T) {
        buffer.appendBytes(mapper.writeValueAsBytes(s))
    }

    override fun decodeFromWire(pos: Int, buffer: Buffer): T {
        val length = buffer.getInt(pos)
        val bytes = buffer.getBytes(pos + 4, pos + 4 + length)
        return mapper.readValue(bytes, clazz)
    }
    ...
}

register codec for each class I want to serialize:

vertx.eventBus()
        .registerDefaultCodec(A::class.java, JacksonCodec(DatabindCodec.mapper(), A::class.java))
    vertx.eventBus()
vertx.eventBus()
        .registerDefaultCodec(B::class.java, JacksonCodec(DatabindCodec.mapper(), B::class.java))
    vertx.eventBus()

The code examples are kotlin but same applies for Java.

2 Answers

It is possible, with some limitations and quirks. I would not recommend doing it.

Let's start with the limitations:

  1. It can not be used in clustered mode
  2. You have to declare the codec name every time you send something over the eventbus.

If you create a generic codec that encodes classes with Jackson and every time you send something over the eventbus you make sure to add it using codecName in the deliveryOptions, you can register it only once and use it for all of your classes.

Full example:

fun main() {
  val vertx = Vertx.vertx()
  vertx.eventBus().registerCodec(GenericCodec())

  vertx.eventBus().consumer<Foo>("test-address") {
    println(it.body())
    it.reply(Bar(), genericDeliveryOptions)
  }

  vertx.eventBus().request<String>("test-address", Foo(), genericDeliveryOptions) {
    println(it.result().body())
  }

  vertx.close()
}

data class Foo(
  val foo: String = "foo",
)

data class Bar(
  val bar: String = "bar",
)

class GenericCodec : MessageCodec<Any, Any> {
  companion object {
    const val NAME = "generic"
  }

  private val mapper: ObjectMapper = ObjectMapper()

  override fun encodeToWire(buffer: Buffer, s: Any) {
    buffer.appendBytes(mapper.writeValueAsBytes(s))
  }

  override fun decodeFromWire(pos: Int, buffer: Buffer): Any {
    throw RuntimeException("should never get here, unless using clustered mode")
  }

  override fun transform(s: Any): Any {
    return s
  }

  override fun name(): String {
    return NAME
  }

  override fun systemCodecID(): Byte {
    return -1
  }
}

val genericDeliveryOptions = deliveryOptionsOf(codecName = GenericCodec.NAME)
Related