quarkusio / quarkus

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

Rest client doesn't get generated when interface annotations are inherited #39286

Open brucejpp opened 8 months ago

brucejpp commented 8 months ago

Describe the bug

Using extensions/resteasy-reactive/rest-client-reactive Follow the tutorial here: https://quarkus.io/guides/rest-client-reactive ...but instead of creating the interface described, create 2 interfaces (A and B). Place the jakarta.ws.rs annotations on interface A, and then make the second interface B extend A. (See attached) At compile time, this failes to build with an error:

[error]: Build step io.quarkus.arc.deployment.ArcProcessor#validate threw an exception: jakarta.enterprise.inject.spi.DeploymentException: jakarta.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type org.acme.rest.client.B and qualifiers [@RestClient]

Expected behavior

The annotations in the superinterface should be inherited and processed correctly

Actual behavior

Superinterface annotations are ignored

How to Reproduce?

Follow the example linked about, but replace the interface class with these two:

package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import java.util.Set;

@Path("/extensions")
public interface A {

    @GET
    Set<Extension> getById(@QueryParam("id") String id);
}
package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import java.util.Set;

@RegisterRestClient
public interface B extends A {

}

Output of uname -a or ver

23.3.0 Darwin Kernel Version 23.3.0: Wed Dec 20 21:31:00 PST 2023; root:xnu-10002.81.5~7/RELEASE_ARM64_T6020 arm64

Output of java -version

java version "17.0.8" 2023-07-18 LTS

Quarkus version or git rev

3.7.2

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

maven 3.9.4

Additional information

No response

quarkus-bot[bot] commented 8 months ago

/cc @cescoffier (rest-client), @geoand (rest-client)

geoand commented 8 months ago

I'm pretty sure we've discussed this in the past...

cescoffier commented 8 months ago

@Ladicek probably knows the outcome of that discussion (because he has a great memory)

brucejpp commented 8 months ago

The jax.rs specification says that the annotations are inherited (section 3.6) which is why it was very confusing to find that these are not: https://download.oracle.com/otn-pub/jcp/jaxrs-2_0_rev_A-mrel-eval-spec/jsr339-jaxrs-2.0-final-spec.pdf?AuthParam=1710314633_0852fc20bcf0438b26aa009ada61dc73

Ladicek commented 8 months ago

I don't think we discussed this particular situation, or at least I don't remember it (and my memory is not as good as Clement might think...), and the MP RestClient specification doesn't mention annotation inheritance at all.

It seems natural to fall back on the JAX-RS rules for annotation inheritance, which RestClient Reactive seems to do: https://github.com/quarkusio/quarkus/blob/3.7.2/extensions/resteasy-reactive/rest-client-reactive/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java#L433

Technically, the code example in the description of this issue doesn't follow the JAX-RS rules for annotation inheritance, because

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

which means that the @Path annotation must be repeated on B (which it isn't), but that doesn't seem to be an issue here.

I'll try to reproduce and see.

Ladicek commented 8 months ago

I can't reproduce. This seems to work both on Quarkus 3.7.2 and on current 999-SNAPSHOT.

brucejpp commented 7 months ago

Ok, my apologies - the scenario as described in my original report DOES indeed work. However...

Additionally, I had a class C in the project, such that:

package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.inject.RestClient;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

@ApplicationScoped
public class C {

    @Inject
    @RestClient
    B bService;

    public C() {

    }

}

Running quarkus dev results in:

2024-03-14 15:55:31,538 ERROR [io.qua.dep.dev.IsolatedDevModeMain] (main) Failed to start quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
    [error]: Build step io.quarkus.arc.deployment.ArcProcessor#validate threw an exception: jakarta.enterprise.inject.spi.DeploymentException: jakarta.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type org.acme.rest.client.B and qualifiers [@RestClient]
    - injection target: org.acme.rest.client.C#bService
    - declared on CLASS bean [types=[org.acme.rest.client.C, java.lang.Object], qualifiers=[@Default, @Any], target=org.acme.rest.client.C]

This does not happen if you modify the injection in C (of B) with the superclass A (with added @RegisterRestClient) Sorry for the confusion.

Ladicek commented 7 months ago

When I was trying to reproduce, I of course tried to inject the sub-interface into a class. It worked flawlessly.

brucejpp commented 7 months ago

ok - I understand what is happening with this issue example on my system. I can repeat the failure in a case I have, but the project structure is a bit different. Should I close this and reopen in another issue or provide complete example here?

Ladicek commented 7 months ago

Feel free to continue in this issue.

brucejpp commented 7 months ago

In my real code where I encountered this problem, I have multiple maven projects. You can refactor the example above in a similar way. Create a new quarkus project using: quarkus create app org.acme:Foo Move the superclass A.java and Extension.java into this new project, and add a dependency in the original rest-client-reactive-quickstart project to this, like:

<dependency>
        <groupId>org.acme</groupId>
        <artifactId>Foo</artifactId>
        <version>1.0.0-SNAPSHOT</version>
</dependency>

Build the project Foo, then run the rest-client-reactive-quickstart project with quarkus dev. You should then see:

2024-03-15 10:06:59,141 ERROR [io.qua.dep.dev.IsolatedDevModeMain] (main) Failed to start quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
    [error]: Build step io.quarkus.arc.deployment.ArcProcessor#validate threw an exception: jakarta.enterprise.inject.spi.DeploymentException: jakarta.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type org.acme.rest.client.B and qualifiers [@RestClient]
    - injection target: org.acme.rest.client.C#bService
    - declared on CLASS bean [types=[org.acme.rest.client.C, java.lang.Object], qualifiers=[@Default, @Any], target=org.acme.rest.client.C]
HiagoW commented 6 months ago

I'm facing a similar issue. I have a RestClient:

package com.test;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

@Path("/api/v2")
@RegisterRestClient
@ApplicationScoped
public interface TestClient {
    @GET
    @Path("/test")
    Response test();
}

And I'm trying to inject this Rest Client:

package com.test;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.rest.client.inject.RestClient;

@ApplicationScoped
public class TestBean {

    @Inject
    @RestClient
    TestClient client;

}

but I'm getting this error:

Caused by: jakarta.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type com.test.TestClient and qualifiers [@RestClient]
        - injection target: com.test.TestBean#client
        - declared on CLASS bean [types=[com.test.TestBean, java.lang.Object], qualifiers=[@Default, @Any], target=com.test.TestBean]
        at io.quarkus.arc.processor.Beans.resolveInjectionPoint(Beans.java:519)
        at io.quarkus.arc.processor.BeanInfo.init(BeanInfo.java:638)
        at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:308)

This only happens if I update from quarkus-resteasy-client and quarkus-resteasy-client-deployment to quarkus-rest-client and quarkus-rest-client-deployment

geoand commented 6 months ago

Does the problem above persist if you remove @ApplicationScoped?

HiagoW commented 6 months ago

@geoand yes, I have tried that, it fails with same error. It only works if I use resteasy-client instead of rest-client. However, as this is an extension, I cannot use resteasy as it conflicts with the new "rest" from Quarkus 3.9 when I add this extension to a Quarkus 3.9 project:

Multiple producers of item class io.quarkus.security.spi.DefaultSecurityCheckBuildItem (io.quarkus.resteasy.reactive.common.deployment.ResteasyReactiveCommonProcessor#setUpDenyAllJaxRs)
geoand commented 6 months ago

That's very weird. Please attach a sample application that exhibits this behavior so we can check.

HiagoW commented 6 months ago

@geoand nvm, I have found the solution on #34180 I was using AdditionalBeanBuildItem instead of AdditionalIndexedClassesBuildItem. It is working now that I have changed. Thanks!