Quarkus: missing RESTeasy classes in native build of JAX-RS app

Created on 7 Feb 2020  路  13Comments  路  Source: quarkusio/quarkus

Describe the bug
A JAX-RS app which works fine when built as a JAR fails when built as a native executable.

Expected behavior
JAX-RS endpoint should work and emit JSON.

Actual behavior
JAX-RS endpoint fails during lazy initialization of CDI beans due to missing classes in RESTeasy.

Additional context
See reproducer here: https://github.com/emattheis/quarkus-native-issue

kinbug

All 13 comments

I see that the code uses:

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public StreamingOutput sayHello()
    {
        return new Greeter(service, config);
    }

which seems to work in JVM mode but not native. In native mode the root cause of the failure seems to be:

020-02-07 12:28:05,773 WARN  [org.jbo.res.res.i18n] (executor-thread-1) RESTEASY002120: ClassNotFoundException: Unable to load builtin provider org.jboss.resteasy.plugins.providers.JaxrsFormProvider from resource:META-INF/services/javax.ws.rs.ext.Providers: java.lang.ClassNotFoundException: org.jboss.resteasy.plugins.providers.JaxrsFormProvider

Update

Actually that is just a warning, I see later on:

Caused by: javax.json.stream.JsonGenerationException: Generating incomplete JSON
    at org.glassfish.json.JsonGeneratorImpl.close(JsonGeneratorImpl.java:511)
    at io.github.emattheis.quarkus.issues.Greeter.write(Greeter.java:38)
    at org.jboss.resteasy.plugins.providers.StreamingOutputProvider.writeTo(StreamingOutputProvider.java:37)
    at org.jboss.resteasy.plugins.providers.StreamingOutputProvider.writeTo(StreamingOutputProvider.java:20)
    at org.jboss.resteasy.core.interception.jaxrs.AbstractWriterInterceptorContext.writeTo(AbstractWriterInterceptorContext.java:193)
    at org.jboss.resteasy.core.interception.jaxrs.ServerWriterInterceptorContext.writeTo(ServerWriterInterceptorContext.java:64)
    at org.jboss.resteasy.core.interception.jaxrs.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:155)
    at org.jboss.resteasy.core.ServerResponseWriter.lambda$writeNomapResponse$2(ServerResponseWriter.java:156)
    at org.jboss.resteasy.core.interception.jaxrs.ContainerResponseContextImpl.filter(ContainerResponseContextImpl.java:404)
    at org.jboss.resteasy.core.ServerResponseWriter.executeFilters(ServerResponseWriter.java:232)
    at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:97)
    at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:70)
    at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:578)

Do we have to do something specific in Quarkus to get this to work in native mode?

Now we can in Quarkus register this provider when we see StreamingOutput as a return value of a JAX-RS method, but I don't understand why it's needed.
@asoldano @FroMage any ideas?

I'm guessing it depends on the contents of Greeter.write which seems to be doing its own serialisation with JsonGeneratorImpl. Probably that lib needs some types registered for reflection.

Oh, I didn't realize that SerializationOutput is something that a user implements! I should have checked that, thanks!

Then this shifts and I think the idea person to take a look is @aguibert :)

@geoand For what it's worth, I found that switching my JAX-RS method from this

@GET
@Produces(MediaType.APPLICATION_JSON)
public StreamingOutput getStream() {
    return new StreamingOutputImpl();
}

to this

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getStream() {
    return Response.ok(new StreamingOutputImpl()).build();
}

makes the Jandex warnings go away. The native build still exhibits the same problems, though.

@aguibert any chance you could have a look at that one?

I'll have a look at this one. Can you assign to me please @geoand ?

Done!

hi @emattheis, been looking into this issue a bit and for starters I'll propose a workaround for you.

There seems to be some issue with directly injecting constructs like Client instead of using a REST Client interface. I was able to get your sample working by changing the GreetingService class to the following:

@ApplicationScoped
public class GreetingService {
    @Inject
    @RestClient
    DummyRestClient dummyRc;

    public Stream<String> getGreetings() {
        dummyRc.ping();
        return Stream.of("Hello, Quarkus!");
    }

    @RegisterRestClient(baseUri = "https://quarkus.io", configKey = "serviceEndpoint")
    public static interface DummyRestClient {
        @HEAD
        public void ping();
    }
}

If I go back to the original code, I see that the root issue is the CNFE's -- the "incomplete JSON" error message is a red-herring caused by the try-catch block on the JsonGenerator. If we remove the try-catch, we get the usual CNFE's in the org.jboss.resteasy.plugins.providers.* package but also get the following NPE:

Caused by: java.lang.NullPointerException
    at org.jboss.resteasy.core.ConstructorInjectorImpl.<init>(ConstructorInjectorImpl.java:55)
    at org.jboss.resteasy.core.InjectorFactoryImpl.createConstructor(InjectorFactoryImpl.java:61)
    at org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl.injectedInstance(ResteasyProviderFactoryImpl.java:1422)
    at org.jboss.resteasy.core.interception.jaxrs.JaxrsInterceptorRegistryImpl$AbstractInterceptorFactory.createInterceptor(JaxrsInterceptorRegistryImpl.java:151)
    at org.jboss.resteasy.core.interception.jaxrs.JaxrsInterceptorRegistryImpl$OnDemandInterceptorFactory.initialize(JaxrsInterceptorRegistryImpl.java:169)
    at org.jboss.resteasy.core.interception.jaxrs.JaxrsInterceptorRegistryImpl$OnDemandInterceptorFactory.checkInitialize(JaxrsInterceptorRegistryImpl.java:184)
    at org.jboss.resteasy.core.interception.jaxrs.JaxrsInterceptorRegistryImpl$OnDemandInterceptorFactory.getInterceptor(JaxrsInterceptorRegistryImpl.java:195)
    at org.jboss.resteasy.core.interception.jaxrs.JaxrsInterceptorRegistryImpl$AbstractInterceptorFactory.postMatch(JaxrsInterceptorRegistryImpl.java:139)
    at org.jboss.resteasy.core.interception.jaxrs.JaxrsInterceptorRegistryImpl.postMatch(JaxrsInterceptorRegistryImpl.java:284)
    at org.jboss.resteasy.client.jaxrs.internal.ClientConfiguration.getRequestFilters(ClientConfiguration.java:116)
    at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.getRequestFilters(ClientInvocation.java:454)
    at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.filterRequest(ClientInvocation.java:671)
    at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:485)
    at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:65)
    at org.jboss.resteasy.client.jaxrs.internal.ClientInvocationBuilder.head(ClientInvocationBuilder.java:262)
    at io.github.emattheis.quarkus.issues.GreetingService.getGreetings(GreetingService.java:39)
    at io.github.emattheis.quarkus.issues.GreetingService_ClientProxy.getGreetings(GreetingService_ClientProxy.zig:50)
    at io.github.emattheis.quarkus.issues.Greeter.write(Greeter.java:39)
        ... <more stack>

Thanks for the followup @aguibert

In my actual application I need a Client to have finer control over HTTP requests, so unfortunately the workaround isn't suitable.

I've posted a proposed fix in PR #7270. The issues ended up being in the REST Client code, not the JSONB code, so I'm not sure who is best to review this. Perhaps @geoand or @FroMage ?

I pinged Ken for a review since he knows more about the client

@emattheis fix is delivered now and it should be included in the next release of Quarkus (1.3.0)

Was this page helpful?
0 / 5 - 0 ratings