2

When I try to return a Uni with a typed java.util.List in Quarkus in dev mode, i get a ClassNotFound exception. I have read about Quarkus using different class loaders in different profiles, but I don't see that I do anything fancy.

Here's the sender

@Query("offers")
public Uni<List<OfferResponse>> getOffers(@PathParam("category") Integer categoryId) {
    OfferRequest event = new OfferRequest();
    event.setCategoryId(categoryId);
    Uni<List<OfferResponse>> offers = bus.<List<OfferResponse>>request(OfferRequest.ADDRESS, event).onItem().transform(Message::body);
    return offers;
}

And here's the consumer

@ConsumeEvent(OfferRequest.ADDRESS)
    public Uni<List<OfferResponse>> onOfferQuery(OfferRequest request) {
        List<KelkooOffer> offers = getOffers(request.getCategoryId());
        List<OfferResponse> responses = new ArrayList<OfferResponse>();
        for (KelkooOffer offer : offers) {
            responses.add(offer.getEventResponse());
        }
        return Uni.createFrom().item(responses);
    }

The bean I'm trying to return is just a POJO

and the error message

2021-08-18 11:11:16,186 ERROR [io.qua.run.Application] (Quarkus Main Thread) Failed to start application (with profile dev): java.lang.ClassNotFoundException: java.util.List<se.bryderi.events.OfferResponse>
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
        at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:414)
        at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:405)
        at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:414)
        at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:405)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:398)
        at io.quarkus.deployment.steps.VertxProcessor$build609260703.deploy_0(VertxProcessor$build609260703.zig:142)
        at io.quarkus.deployment.steps.VertxProcessor$build609260703.deploy(VertxProcessor$build609260703.zig:40)
        at io.quarkus.runner.ApplicationImpl.doStart(ApplicationImpl.zig:784)
        at io.quarkus.runtime.Application.start(Application.java:101)
        at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:101)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:66)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:42)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:119)
        at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:29)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at io.quarkus.runner.bootstrap.StartupActionImpl$1.run(StartupActionImpl.java:98)
        at java.base/java.lang.Thread.run(Thread.java:829)

I get the same result if I run the dev profile or if I run the packaged fast-jar.

Happy for any hint that will point me in the right direction

Mattias Johansson
  • 723
  • 2
  • 6
  • 22
  • Does this pojo comes from another jar or is present in the same project ? – Javier Toja Aug 18 '21 at 09:25
  • It's in the same project, I can also compile the project fine in Maven, it's only when trying to start up it does not work – Mattias Johansson Aug 18 '21 at 09:27
  • Have you tried using codecs? https://quarkus.io/guides/reactive-event-bus#using-codecs . – Javier Toja Aug 18 '21 at 09:34
  • No, I have not. I can see that it registers my Pojos as codecs just before it stops [INFO] [io.quarkus.vertx.deployment.EventBusCodecProcessor] Local Message Codec registered for type se.bryderi.events.OfferRequest [INFO] [io.quarkus.vertx.deployment.EventBusCodecProcessor] Local Message Codec registered for type java.util.List – Mattias Johansson Aug 18 '21 at 09:42
  • How are you running your project? – Pendula Aug 18 '21 at 10:07
  • I have tried both. mvnw package and then run the generated JAR-file, and ./mvnw quarkus:dev, both with the same result – Mattias Johansson Aug 18 '21 at 11:12
  • I have created a minimal project that replicates my issue: https://github.com/matt1as/quarkus-event-test The main branch, where I return an instance of my Pojo works, but when trying to return a List of Pojos I do get the exception. This might be the intended behaviour, but I cannot find it in any documentation. See the branch https://github.com/matt1as/quarkus-event-test/tree/list_of_greetings for a non working version. – Mattias Johansson Aug 18 '21 at 12:25

1 Answers1

3

There is a limitation of the Vert.x EventBus which prevents to encode/decode lists or sets directly.

You could create a wrapper for your OfferResponse list:

import java.util.AbstractList;
import java.util.List;

public class OfferResponseList extends AbstractList<OfferResponse> {

    private List<OfferResponse> wrapped;

    private OfferResponseList(List<OfferResponse> wrapped) {
        this.wrapped = wrapped;
    }

    public static OfferResponseList wrap(List<OfferResponse> list) {
        return new OfferResponseList(list);
    }

    @Override
    public OfferResponse get(int index) {
        return wrapped.get(index);
    }

    @Override
    public int size() {
        return wrapped.size();
    }
}

Then transform your consumer to:

@ConsumeEvent(OfferRequest.ADDRESS)
public Uni<OfferResponseList> onOfferQuery(OfferRequest request) {
    List<KelkooOffer> offers = getOffers(request.getCategoryId());
    List<OfferResponse> responses = new ArrayList<OfferResponse>();
    for (KelkooOffer offer : offers) {
        responses.add(offer.getEventResponse());
    }
    // Notice the call to OfferResponseList.wrap here
    return Uni.createFrom().item(OfferResponseList.wrap(responses));
}

That's it. Quarkus will register a codec for OfferResponseList automatically.

On the client side, you do not need to change your code:

@Query("offers")
public Uni<List<OfferResponse>> getOffers(@PathParam("category") Integer categoryId) {
    OfferRequest event = new OfferRequest();
    event.setCategoryId(categoryId);
    // You can use Uni<List<OfferResponse>> or Uni<OfferResponseList>
    Uni<List<OfferResponse>> offers = bus.<List<OfferResponse>>request(OfferRequest.ADDRESS, event).onItem().transform(Message::body);
    return offers;
}
tsegismont
  • 8,591
  • 1
  • 17
  • 27
  • You're welcome. I've edited the consumer signature because actually it works if the method returns `Uni`, not `Uni>`. – tsegismont Aug 19 '21 at 09:08