Search code examples
javavert.xvertx-eventbus

What is the best way to send a collection of objects over the Event Bus in Vertx?


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!


Solution

  • 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.