1

I have a handler that serves HTTP requests at a given endpoint. The handler messages a verticle via the event bus that makes some external paginated REST calls, aggregates the results, and returns the results back to the handler. The result of the paginated REST calls is represented as a List of custom objects. If I just try to send the List itself, Vertx throws an exception complaining that it can't find a codec for java.util.ArrayList.

I'm trying to find the "best" -- meaning the easiest, most efficient, and most readable/maintainable -- way in Vertx to send a list of these objects back across the event bus to my handler. These are the options I know of and have tried so far, are there better ways to achieve this?

  1. Serialize list to JSON and store in a JsonObject. This requires an explicit serialization/deserialization on either end which seems unnecessary:
// Verticle
List<CustomObject> result = method();
JsonObject data = new JsonObject();
data.put("result", Json.encode(result));
msg.reply(data);

// Handler
String serializedList = body.getString("result");
List<CustomObject> list = objectMapper.readValue(serializedList, new TypeReference<List<CustomObject>>(){});

  1. Define a message codec for ArrayList<CustomObject>. In theory I believe this would work, but all the examples I've seen online for message codecs are always about creating a codec for a single object, and I'm not entirely sure if this would work for collections.

Is there a simpler method that fits my use case that I'm unaware of? Thanks!

Daniel Kesner
  • 226
  • 4
  • 16
  • Do you use clustered event bus? If not, you can just create holder object, no serialization will occur. – Alexey Soshin Nov 20 '19 at 21:11
  • I don't use a clustered event bus. Could you clarify what you mean by "holder object"? Could you post a code snippet showing what you mean? – Daniel Kesner Nov 20 '19 at 22:51

1 Answers1

2

Sorry for a lengthy example, but here you go:

public class EventBusHolder {

    public static void main(String[] args) {
        Vertx vertx = Vertx.vertx();

        vertx.eventBus().registerDefaultCodec(Holder.class, new HolderCodec());
        vertx.deployVerticle(new SomeVerticle(), (r) -> {
            vertx.eventBus().send("custom", new Holder(new CustomObject("a")));
        });
    }
}

class HolderCodec implements MessageCodec<Holder, Holder> {

    @Override
    public void encodeToWire(Buffer buffer, Holder holder) {

    }

    @Override
    public Holder decodeFromWire(int pos, Buffer buffer) {
        return null;
    }

    @Override
    public Holder transform(Holder holder) {
        return holder;
    }

    @Override
    public String name() {
        return "HolderCodec";
    }

    @Override
    public byte systemCodecID() {
        return -1;
    }
}

class SomeVerticle extends AbstractVerticle {

    @Override
    public void start() {
        vertx.eventBus().consumer("custom", (msg) -> {
           System.out.println(msg.body());
        });
    }
}

class CustomObject {
    public String name;

    public CustomObject(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "CustomObject{" +
                "name='" + name + '\'' +
                '}';
    }
}


final class Holder {
    @Override
    public String toString() {
        return "Holder{" +
                "data=" + data +
                '}';
    }

    private final List<CustomObject> data;

    public Holder(final CustomObject... data) {
        this.data = Arrays.asList(data);
    }

    public List<CustomObject> getData() {
        return data;
    }
}

Take note that encodeToWire and decodeFromWire are not implemented. They aren't invoked for local messages.

Having this Holder object is an easy way to get around type erasure on the JVM.

Alexey Soshin
  • 16,718
  • 2
  • 31
  • 40
  • I see, that makes more sense. And within the Verticle that receives the event bus message, I can just pull out the `Holder` object directly from the message body, like `Holder holder = (Holder) msg.body()`? And then grab the underlying list inside with `List list = holder.getData()`? – Daniel Kesner Nov 26 '19 at 00:09
  • Yes, because you're in the same JVM process. – Alexey Soshin Nov 26 '19 at 10:19
  • 1
    Thanks, this should do exactly what I need to do then. FYI I've typically seen the 'Holder' class you created referred to as a 'wrapper', which is why I was a little confused when you first commented. Thanks! – Daniel Kesner Dec 03 '19 at 21:12
  • Wrapper is for functionality, Holder is for data. Was happy to help :) – Alexey Soshin Dec 03 '19 at 21:37