diff --git a/docs/src/main/asciidoc/rest-client-guide.adoc b/docs/src/main/asciidoc/rest-client-guide.adoc index 3f52b58f0d82d..edc7622454d59 100644 --- a/docs/src/main/asciidoc/rest-client-guide.adoc +++ b/docs/src/main/asciidoc/rest-client-guide.adoc @@ -157,7 +157,8 @@ The `getByName` method gives our code the ability to query a country by name fro The purpose of the annotations in the code above is the following: -* `@RegisterRestClient` allows {project-name} to know that this interface is meant to be used as a REST Client +* `@RegisterRestClient` allows {project-name} to know that this interface is meant to be available for +CDI injection as a REST Client * `@Path`, `@GET` and `@PathParam` are the standard JAX-RS annotations used to define how to access the service * `@Produces` defines the expected content-type @@ -268,3 +269,7 @@ You should see a JSON object containing some basic information about Greece. As usual, the application can be packaged using `mvn clean package` and executed using the `-runner.jar` file. You can also generate the native executable with `mvn clean package -Pnative`. + +== Further reading + + * link:https://download.eclipse.org/microprofile/microprofile-rest-client-1.2.1/microprofile-rest-client-1.2.1.html[MicroProfile Rest Client specification] diff --git a/extensions/smallrye-rest-client/deployment/src/main/java/io/quarkus/smallrye/restclient/deployment/SmallRyeRestClientProcessor.java b/extensions/smallrye-rest-client/deployment/src/main/java/io/quarkus/smallrye/restclient/deployment/SmallRyeRestClientProcessor.java index 2554af862ef43..2ae234fc68654 100644 --- a/extensions/smallrye-rest-client/deployment/src/main/java/io/quarkus/smallrye/restclient/deployment/SmallRyeRestClientProcessor.java +++ b/extensions/smallrye-rest-client/deployment/src/main/java/io/quarkus/smallrye/restclient/deployment/SmallRyeRestClientProcessor.java @@ -22,18 +22,19 @@ import java.util.Map; import java.util.Set; +import javax.ws.rs.Path; import javax.ws.rs.client.ClientRequestFilter; import javax.ws.rs.client.ClientResponseFilter; import javax.ws.rs.ext.Providers; import org.apache.commons.logging.impl.Jdk14Logger; import org.apache.commons.logging.impl.LogFactoryImpl; -import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import org.eclipse.microprofile.rest.client.inject.RestClient; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; import org.jboss.jandex.MethodInfo; import org.jboss.jandex.Type; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; @@ -47,7 +48,6 @@ import io.quarkus.arc.processor.BeanConfigurator; import io.quarkus.arc.processor.BeanRegistrar; import io.quarkus.arc.processor.BuiltinScope; -import io.quarkus.arc.processor.ScopeInfo; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.ExecutionTime; @@ -73,7 +73,7 @@ class SmallRyeRestClientProcessor { private static final DotName REST_CLIENT = DotName.createSimple(RestClient.class.getName()); - private static final DotName REGISTER_REST_CLIENT = DotName.createSimple(RegisterRestClient.class.getName()); + private static final DotName PATH = DotName.createSimple(Path.class.getName()); private static final String PROVIDERS_SERVICE_FILE = "META-INF/services/" + Providers.class.getName(); @@ -141,7 +141,8 @@ void processInterfaces(CombinedIndexBuildItem combinedIndexBuildItem, Map interfaces = new HashMap<>(); Set returnTypes = new HashSet<>(); - for (AnnotationInstance annotation : combinedIndexBuildItem.getIndex().getAnnotations(REGISTER_REST_CLIENT)) { + IndexView index = combinedIndexBuildItem.getIndex(); + for (AnnotationInstance annotation : index.getAnnotations(PATH)) { AnnotationTarget target = annotation.target(); ClassInfo theInfo; if (target.kind() == AnnotationTarget.Kind.CLASS) { @@ -151,9 +152,11 @@ void processInterfaces(CombinedIndexBuildItem combinedIndexBuildItem, } else { continue; } - if (!Modifier.isInterface(theInfo.flags())) { + + if (!isRestClientInterface(index, theInfo)) { continue; } + interfaces.put(theInfo.name(), theInfo); // Find Return types @@ -212,4 +215,9 @@ public void register(RegistrationContext registrationContext) { extensionSslNativeSupport.produce(new ExtensionSslNativeSupportBuildItem(FeatureBuildItem.SMALLRYE_REST_CLIENT)); RestClientBuilderImpl.SSL_ENABLED = sslNativeConfig.isEnabled(); } + + private boolean isRestClientInterface(IndexView index, ClassInfo classInfo) { + return Modifier.isInterface(classInfo.flags()) + && index.getAllKnownImplementors(classInfo.name()).isEmpty(); + } } diff --git a/integration-tests/main/src/main/java/io/quarkus/example/rest/ClientResource.java b/integration-tests/main/src/main/java/io/quarkus/example/rest/ClientResource.java index a45baa50a4311..7a27ad354f655 100644 --- a/integration-tests/main/src/main/java/io/quarkus/example/rest/ClientResource.java +++ b/integration-tests/main/src/main/java/io/quarkus/example/rest/ClientResource.java @@ -37,9 +37,9 @@ public class ClientResource { @GET @Path("/manual") public String manual() throws Exception { - RestInterface iface = RestClientBuilder.newBuilder() + ProgrammaticRestInterface iface = RestClientBuilder.newBuilder() .baseUrl(new URL(System.getProperty("test.url"))) - .build(RestInterface.class); + .build(ProgrammaticRestInterface.class); return iface.get(); } @@ -53,9 +53,9 @@ public String cdi() throws Exception { @Path("manual/jackson") @Produces("application/json") public TestResource.MyData getDataManual() throws Exception { - RestInterface iface = RestClientBuilder.newBuilder() + ProgrammaticRestInterface iface = RestClientBuilder.newBuilder() .baseUrl(new URL(System.getProperty("test.url"))) - .build(RestInterface.class); + .build(ProgrammaticRestInterface.class); System.out.println(iface.getData()); return iface.getData(); } @@ -71,9 +71,9 @@ public TestResource.MyData getDataCdi() { @Path("/manual/complex") @Produces("application/json") public List complexManual() throws Exception { - RestInterface iface = RestClientBuilder.newBuilder() + ProgrammaticRestInterface iface = RestClientBuilder.newBuilder() .baseUrl(new URL(System.getProperty("test.url"))) - .build(RestInterface.class); + .build(ProgrammaticRestInterface.class); System.out.println(iface.complex()); return iface.complex(); } diff --git a/integration-tests/main/src/main/java/io/quarkus/example/rest/ProgrammaticRestInterface.java b/integration-tests/main/src/main/java/io/quarkus/example/rest/ProgrammaticRestInterface.java new file mode 100644 index 0000000000000..dcf526182326d --- /dev/null +++ b/integration-tests/main/src/main/java/io/quarkus/example/rest/ProgrammaticRestInterface.java @@ -0,0 +1,28 @@ +package io.quarkus.example.rest; + +import java.util.List; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +/** + * A version of {@link RestInterface} that doesn't have {@link org.eclipse.microprofile.rest.client.inject.RegisterRestClient} + * and can be used only programmatically, i.e. with the builder. + */ +@Path("/test") +public interface ProgrammaticRestInterface { + + @GET + String get(); + + @GET + @Path("/jackson") + @Produces("application/json") + TestResource.MyData getData(); + + @GET + @Path("/complex") + @Produces("application/json") + List complex(); +}