Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RESTEasy tries to instantiate abstract resource base class #12927

Closed
famod opened this issue Oct 25, 2020 · 14 comments · Fixed by #12963
Closed

RESTEasy tries to instantiate abstract resource base class #12927

famod opened this issue Oct 25, 2020 · 14 comments · Fixed by #12963
Labels
area/resteasy-classic kind/bug Something isn't working
Milestone

Comments

@famod
Copy link
Member

famod commented Oct 25, 2020

Describe the bug
If your REST resource extends an abstract base class that contains all the typical annotations like @Path, @GET etc. then you'll see this failure when invoking the resource:

2020-10-25 23:39:01,178 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /hello failed, error id: fb45d7cc-5fa1-4aa5-ae5c-fa458d3b702d-1: org.jboss.resteasy.spi.UnhandledException: java.lang.RuntimeException: java.lang.InstantiationException
        at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:381)
        at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:218)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:519)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
        at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
        at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:131)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.access$000(VertxRequestHandler.java:37)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:94)
        at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
        at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2046)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1578)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at java.base/java.lang.Thread.run(Thread.java:834)
        at org.jboss.threads.JBossThread.run(JBossThread.java:479)
Caused by: java.lang.RuntimeException: java.lang.InstantiationException
        at io.quarkus.arc.runtime.BeanContainerImpl$DefaultInstanceFactory.create(BeanContainerImpl.java:75)
        at io.quarkus.resteasy.common.runtime.QuarkusConstructorInjector.construct(QuarkusConstructorInjector.java:54)
        at org.jboss.resteasy.plugins.server.resourcefactory.POJOResourceFactory.createResource(POJOResourceFactory.java:71)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:386)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:68)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492)
        ... 17 more
Caused by: java.lang.InstantiationException
        at java.base/jdk.internal.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
        at java.base/java.lang.Class.newInstance(Class.java:584)
        at io.quarkus.arc.runtime.BeanContainerImpl$DefaultInstanceFactory.create(BeanContainerImpl.java:67)
        ... 22 more

Such abstract base classes can be the result of https://github.com/OpenAPITools/openapi-generator

Expected behavior
Resource classes can use abstract base classes (that contain JAX-RS annotations) without problems.

Actual behavior
Invocation of such a resource fails.

To Reproduce

Steps to reproduce the behavior:

  1. git clone https://github.com/famod/quarkus-quickstarts
  2. git switch rest-abstract
  3. cd getting-started
  4. mvn clean verify (will fail with the mentioned error)
  5. mvn clean package -DskipTests
  6. java -jar target/getting-started-1.0-SNAPSHOT-runner.jar
  7. curl localhost:8080/hello (will fail with the mentioned error)

Configuration
not relevant

Environment (please complete the following information):

  • Output of uname -a or ver: MINGW64_NT-10.0-18363 XXX 3.0.7-338.x86_64 2019-11-21 23:07 UTC x86_64 Msys
  • Output of java -version: OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.8+10, mixed mode)
  • GraalVM version (if different from Java): n/a
  • Quarkus version or git rev: 1.9.0.Final
  • Build tool (ie. output of mvnw --version or gradlew --version): Apache Maven 3.6.3

Additional context
Interfaces do work but annotation discovery has some blind spots in that case:

@famod famod added kind/bug Something isn't working area/resteasy-classic labels Oct 25, 2020
@famod
Copy link
Member Author

famod commented Oct 25, 2020

/cc @patriot1burke @gsmet

@stuartwdouglas
Copy link
Member

hmm, this will work for interfaces, but the spec does not require it to work this way for abstract classes.

@famod
Copy link
Member Author

famod commented Oct 26, 2020

@stuartwdouglas

but the spec does not require it to work this way for abstract classes

Well, I found this: https://stackoverflow.com/a/25924310/9529981

@mickroll
Copy link
Contributor

To resolve quoted quoting:

3.6 Annotation Inheritance
JAX-RS annotations may be used on the methods and method parameters of a super-class or an implemented interface. Such annotations are inherited by a corresponding sub-class or implementation class method provided that the method and its parameters do not have any JAX-RS annotations of their own. Annotationson a super-class take precedence over those on an implemented interface. The precedence over conflicting annotations defined in multiple implemented interfaces is implementation specific. Note that inheritance ofclass or interface annotations is not supported. If a subclass or implementation method has any JAX-RS annotations then all of the annotations on the superclass or interface method are ignored

see https://download.oracle.com/otn-pub/jcp/jaxrs-2_0-fr-eval-spec/jsr339-jaxrs-2.0-final-spec.pdf

@famod
Copy link
Member Author

famod commented Oct 26, 2020

May I kindly ask you @asoldano for an opinion from the RESTeasy POV?

@patriot1burke
Copy link
Contributor

This is supposed to work. Quarkus is probably adding the abstract class as a resource by mistake.

@patriot1burke
Copy link
Contributor

Can you post a simplified example of what triggers the problem? I want to make sure I'm testing all the scenarios.

@famod
Copy link
Member Author

famod commented Oct 26, 2020

Can you post a simplified example of what triggers the problem?

Even simpler than what I described under "To Reproduce"? No, sorry, not at the moment.
Or do you mean something closer to what we are using in our internal project (that is: openapi generated classes)?

@patriot1burke
Copy link
Contributor

Apologies!!! I need to learn to read. :)

@famod
Copy link
Member Author

famod commented Oct 26, 2020

No worries, it happens to all of us! 😁

@patriot1burke
Copy link
Contributor

Pretty sure this is the issue:

" Note that inheritance of class or interface annotations is not supported."

Concrete classes must specify a @path annotation at the class level. (FYI, I was on JAX-RS 2.0 spec).

@path("/foo")
public class Concrete extends MyAbstract {
}

Quarkus is spitting out the wrong error message, but this wouldn't be supported in Resteasy anyways.

@patriot1burke
Copy link
Contributor

FYI, the reason for requiring @path on the concrete class is for cases where multiple classes extended the same abstract class (or interface), IIRC.

@famod
Copy link
Member Author

famod commented Oct 26, 2020

Interesting! I can confirm that moving @Path from the abstract class to the concrete class fixes the problem.
Additionally, a method level @Path annotation in the abstract class is (still) picked up correctly.

But why is a "class level" @Path working if defined in an interface instead of an abstract class?
I mean, it is good that it's working this way because that's what openapi generator is spitting out (when using interface mode).
Otherwise we would have to annotate each and every of our concrete resource classes.

PS: All this also means that openapi-generator producing class level @Path in abstract classes is pointless and misleading.
I am not saying openapi generator is free of problems/bugs but it's all a bit confusing.

@patriot1burke
Copy link
Contributor

Nevermind, looks like Resteasy supports that case too. Sorry, its been so long since I've had to deal with this (10years). Patch incoming.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/resteasy-classic kind/bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants