quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.87k stars 2.71k forks source link

ClassCastException with RESTEasy reactive and a Jersey client #36024

Closed jmini closed 1 year ago

jmini commented 1 year ago

Describe the bug

A quarkus app with endpoints using RESTEasy reactive using the org.jboss.resteasy.reactive.ResponseStatus annotation and a Jersey client is not working.

Expected behavior

Can this be supported?

If yes, it would be nice if adding the Jersey client would not impact the quarkus part.

Actual behavior

When the endpoint is called:

ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /hello failed, error id: b4f7ffaf-b82e-4550-802e-1244e1f8aceb-1: java.lang.ClassCastException: class org.glassfish.jersey.message.internal.OutboundJaxrsResponse$Builder cannot be cast to class org.jboss.resteasy.reactive.server.jaxrs.ResponseBuilderImpl (org.glassfish.jersey.message.internal.OutboundJaxrsResponse$Builder and org.jboss.resteasy.reactive.server.jaxrs.ResponseBuilderImpl are in unnamed module of loader io.quarkus.bootstrap.classloading.QuarkusClassLoader @aecb35a)
        at org.jboss.resteasy.reactive.server.handlers.ResponseHandler$1.get(ResponseHandler.java:155)
        at org.jboss.resteasy.reactive.server.core.ServerSerialisers.encodeResponseHeaders(ServerSerialisers.java:503)
        at org.jboss.resteasy.reactive.server.core.ServerSerialisers$1.accept(ServerSerialisers.java:77)
        at org.jboss.resteasy.reactive.server.core.ServerSerialisers$1.accept(ServerSerialisers.java:74)
        at org.jboss.resteasy.reactive.server.vertx.VertxResteasyReactiveRequestContext.handle(VertxResteasyReactiveRequestContext.java:468)
        at org.jboss.resteasy.reactive.server.vertx.VertxResteasyReactiveRequestContext.handle(VertxResteasyReactiveRequestContext.java:45)
        at io.vertx.ext.web.impl.RoutingContextImpl.lambda$null$0(RoutingContextImpl.java:495)
        at io.vertx.ext.web.impl.SparseArray.forEachInReverseOrder(SparseArray.java:41)
        at io.vertx.ext.web.impl.RoutingContextImpl.lambda$getHeadersEndHandlers$1(RoutingContextImpl.java:495)
        at io.vertx.core.http.impl.Http1xServerResponse.prepareHeaders(Http1xServerResponse.java:732)
        at io.vertx.core.http.impl.Http1xServerResponse.end(Http1xServerResponse.java:430)
        at io.vertx.core.http.impl.Http1xServerResponse.end(Http1xServerResponse.java:415)
        at org.jboss.resteasy.reactive.server.vertx.VertxResteasyReactiveRequestContext.end(VertxResteasyReactiveRequestContext.java:370)
        at org.jboss.resteasy.reactive.server.providers.serialisers.ServerStringMessageBodyHandler.writeResponse(ServerStringMessageBodyHandler.java:26)
        at org.jboss.resteasy.reactive.server.core.ServerSerialisers.invokeWriter(ServerSerialisers.java:227)
        at org.jboss.resteasy.reactive.server.core.ServerSerialisers.invokeWriter(ServerSerialisers.java:195)
        at org.jboss.resteasy.reactive.server.core.serialization.FixedEntityWriter.write(FixedEntityWriter.java:28)
        at org.jboss.resteasy.reactive.server.handlers.ResponseWriterHandler.handle(ResponseWriterHandler.java:34)
        at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:147)
        at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:577)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:833)

How to Reproduce?

Step to reproduce:

  1. Create a project with resteasy-reactive link on code.quarkus.io to create the project.
  2. Add @ResponseStatus(201) on the hello endpoint
  3. Add an implementation org.glassfish.jersey.core:jersey-common:3.1.3 dependency (in my real project I add a java REST client that has library as dependency)
  4. Call the endpoint curl http://localhost:8080/hello

Output of uname -a or ver

Darwin 22.3.0 Darwin Kernel Version 22.3.0: Mon Jan 30 20:42:11 PST 2023; root:xnu-8792.81.3~2/RELEASE_X86_64 x86_64

Output of java -version

openjdk version "17.0.5" 2022-10-18
OpenJDK Runtime Environment Temurin-17.0.5+8 (build 17.0.5+8)
OpenJDK 64-Bit Server VM Temurin-17.0.5+8 (build 17.0.5+8, mixed mode, sharing)

Quarkus version or git rev

3.3.3

Build tool (ie. output of mvnw --version or gradlew --version)

------------------------------------------------------------
Gradle 8.1.1
------------------------------------------------------------

Build time:   2023-04-21 12:31:26 UTC
Revision:     1cf537a851c635c364a4214885f8b9798051175b

Kotlin:       1.8.10
Groovy:       3.0.15
Ant:          Apache Ant(TM) version 1.10.11 compiled on July 10 2021
JVM:          17.0.5 (Eclipse Adoptium 17.0.5+8)
OS:           Mac OS X 13.2.1 x86_64

Additional information

Reproducer project https://github.com/jmini/quarkus-experiments/tree/main/quarkus_issue36024

quarkus-bot[bot] commented 1 year ago

/cc @FroMage (resteasy-reactive), @evanchooly (kotlin), @geoand (kotlin,resteasy-reactive), @stuartwdouglas (resteasy-reactive)

geoand commented 1 year ago

You can't use the Jersey client with RESTEasy Reactive. You either use our implementatio of the JAX-RS client (quarkus-jaxrs-client-reactive) or our implemenation of the Microprofile REST Client (quarkus-rest-client-reactive).

jmini commented 1 year ago

@geoand thank you for the quick confirmation. I already thought this would be the case, but it is not written in the documentation.


If the Jersey part is something that I can change. The client library already exists (and is not something I can change) and is embedding Jersey jars as transitive dependencies.

What are my options at quarkus level? Use reative-routes to write the endpoint?


An other check I could do is to verify if the client is really relying on Jersey, or if they are just using the JAX-RS api. In that case I might be able to exchange the lib implementing the JAX-RS API. Not sure this would work 🤔...


This topic of the embedded java HTTP clients is really something that make me think a lot. I started this discussion: How to write a REST client library that is framework agnostic but never got any answer.

geoand commented 1 year ago

It turns out that starting Quarkus 3.4.0 your use case works somewhat better (thank to https://github.com/quarkusio/quarkus/pull/35558), but I'll reopen the issue and see if things can be improved further.

Now WRT framework agnostic clients, I like what Fabric8 Kubernetes Client, Testcontainers and OpenTelemetry have done: That is to create an SPI and then have various implementations (like OkHttp, JDK HttpClient, Vert.x etc). That also allows a framework like Quarkus to even plug in its own implementation.

geoand commented 1 year ago

36025 fixes your issue.