diff --git a/extensions/oidc-client-reactive-filter/deployment/src/main/java/io/quarkus/oidc/client/reactive/filter/deployment/OidcClientReactiveFilterBuildStep.java b/extensions/oidc-client-reactive-filter/deployment/src/main/java/io/quarkus/oidc/client/reactive/filter/deployment/OidcClientReactiveFilterBuildStep.java index 6c5bfd560a1c8..9ccbdfbcce872 100644 --- a/extensions/oidc-client-reactive-filter/deployment/src/main/java/io/quarkus/oidc/client/reactive/filter/deployment/OidcClientReactiveFilterBuildStep.java +++ b/extensions/oidc-client-reactive-filter/deployment/src/main/java/io/quarkus/oidc/client/reactive/filter/deployment/OidcClientReactiveFilterBuildStep.java @@ -4,6 +4,7 @@ import io.quarkus.deployment.Feature; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.AdditionalIndexedClassesBuildItem; import io.quarkus.deployment.builditem.EnableAllSecurityServicesBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; @@ -24,8 +25,11 @@ EnableAllSecurityServicesBuildItem security() { @BuildStep(onlyIf = IsEnabled.class) void registerProvider(BuildProducer additionalBeans, - BuildProducer reflectiveClass) { + BuildProducer reflectiveClass, + BuildProducer additionalIndexedClassesBuildItem) { additionalBeans.produce(AdditionalBeanBuildItem.unremovableOf(OidcClientRequestReactiveFilter.class)); + additionalIndexedClassesBuildItem + .produce(new AdditionalIndexedClassesBuildItem(OidcClientRequestReactiveFilter.class.getName())); reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, OidcClientRequestReactiveFilter.class)); } } diff --git a/extensions/oidc-client-reactive-filter/runtime/src/main/java/io/quarkus/oidc/client/reactive/filter/OidcClientRequestReactiveFilter.java b/extensions/oidc-client-reactive-filter/runtime/src/main/java/io/quarkus/oidc/client/reactive/filter/OidcClientRequestReactiveFilter.java index 20a1e57005c02..7508e906ecb39 100644 --- a/extensions/oidc-client-reactive-filter/runtime/src/main/java/io/quarkus/oidc/client/reactive/filter/OidcClientRequestReactiveFilter.java +++ b/extensions/oidc-client-reactive-filter/runtime/src/main/java/io/quarkus/oidc/client/reactive/filter/OidcClientRequestReactiveFilter.java @@ -8,7 +8,6 @@ import javax.ws.rs.Priorities; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; -import javax.ws.rs.ext.Provider; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; @@ -20,7 +19,6 @@ import io.quarkus.oidc.client.runtime.DisabledOidcClientException; import io.quarkus.oidc.common.runtime.OidcConstants; -@Provider @Priority(Priorities.AUTHENTICATION) public class OidcClientRequestReactiveFilter extends AbstractTokensProducer implements ResteasyReactiveClientRequestFilter { private static final Logger LOG = Logger.getLogger(OidcClientRequestReactiveFilter.class); diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java index b430fb8a37541..78f3f96305a3a 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java @@ -22,6 +22,8 @@ import javax.enterprise.context.SessionScoped; import javax.enterprise.inject.Typed; import javax.inject.Singleton; +import javax.ws.rs.Priorities; +import javax.ws.rs.RuntimeType; import javax.ws.rs.core.MediaType; import org.eclipse.microprofile.config.Config; @@ -178,9 +180,14 @@ void registerHeaderFactoryBeans(CombinedIndexBuildItem index, } /** - * Creates an implementation of `AnnotationRegisteredProviders` class with a constructor that - * puts all the providers registered by the @RegisterProvider annotation in a - * map using the {@link AnnotationRegisteredProviders#addProviders(String, Map)} method + * Creates an implementation of `AnnotationRegisteredProviders` class with a constructor that: + *
    + *
  • puts all the providers registered by the @RegisterProvider annotation in a + * map using the {@link AnnotationRegisteredProviders#addProviders(String, Map)} method
  • + *
  • registers all the provider implementations annotated with @Provider using + * {@link AnnotationRegisteredProviders#addGlobalProvider(Class, int)}
  • + *
+ * * * @param indexBuildItem index * @param generatedBeans build producer for generated beans @@ -188,7 +195,8 @@ void registerHeaderFactoryBeans(CombinedIndexBuildItem index, @BuildStep void registerProvidersFromAnnotations(CombinedIndexBuildItem indexBuildItem, BuildProducer generatedBeans, - BuildProducer unremovableBeans) { + BuildProducer unremovableBeans, + RestClientReactiveConfig clientConfig) { String annotationRegisteredProvidersImpl = AnnotationRegisteredProviders.class.getName() + "Implementation"; IndexView index = indexBuildItem.getIndex(); Map> annotationsByClassName = new HashMap<>(); @@ -217,14 +225,48 @@ void registerProvidersFromAnnotations(CombinedIndexBuildItem indexBuildItem, constructor.invokeSpecialMethod(MethodDescriptor.ofConstructor(AnnotationRegisteredProviders.class), constructor.getThis()); + if (clientConfig.providerAutodiscovery) { + for (AnnotationInstance instance : index.getAnnotations(ResteasyReactiveDotNames.PROVIDER)) { + ClassInfo providerClass = instance.target().asClass(); + + // ignore providers annotated with `@ConstrainedTo(SERVER)` + AnnotationInstance constrainedToInstance = providerClass + .classAnnotation(ResteasyReactiveDotNames.CONSTRAINED_TO); + if (constrainedToInstance != null) { + if (RuntimeType.valueOf(constrainedToInstance.value().asEnum()) == RuntimeType.SERVER) { + continue; + } + } + + if (providerClass.interfaceNames().contains(ResteasyReactiveDotNames.FEATURE)) { + continue; // features should not be automatically registered for the client, see javadoc for Feature + } + + int priority = getAnnotatedPriority(index, providerClass.name().toString(), Priorities.USER); + + constructor.invokeVirtualMethod( + MethodDescriptor.ofMethod(AnnotationRegisteredProviders.class, "addGlobalProvider", + void.class, Class.class, + int.class), + constructor.getThis(), constructor.loadClass(providerClass.name().toString()), + constructor.load(priority)); + } + } + for (Map.Entry> annotationsForClass : annotationsByClassName.entrySet()) { ResultHandle map = constructor.newInstance(MethodDescriptor.ofConstructor(HashMap.class)); for (AnnotationInstance value : annotationsForClass.getValue()) { String className = value.value().asString(); - AnnotationValue priority = value.value("priority"); + AnnotationValue priorityAnnotationValue = value.value("priority"); + int priority; + if (priorityAnnotationValue == null) { + priority = getAnnotatedPriority(index, className, Priorities.USER); + } else { + priority = priorityAnnotationValue.asInt(); + } constructor.invokeInterfaceMethod(MAP_PUT, map, constructor.loadClass(className), - constructor.load(priority == null ? -1 : priority.asInt())); + constructor.load(priority)); } constructor.invokeVirtualMethod( MethodDescriptor.ofMethod(AnnotationRegisteredProviders.class, "addProviders", void.class, String.class, @@ -238,6 +280,21 @@ void registerProvidersFromAnnotations(CombinedIndexBuildItem indexBuildItem, unremovableBeans.produce(UnremovableBeanBuildItem.beanClassNames(annotationRegisteredProvidersImpl)); } + private int getAnnotatedPriority(IndexView index, String className, int defaultPriority) { + ClassInfo providerClass = index.getClassByName(DotName.createSimple(className)); + int priority = defaultPriority; + if (providerClass == null) { + log.warnv("Unindexed provider class {0}. The priority of the provider will be set to {1}. ", className, + defaultPriority); + } else { + AnnotationInstance priorityAnnoOnProvider = providerClass.classAnnotation(ResteasyReactiveDotNames.PRIORITY); + if (priorityAnnoOnProvider != null) { + priority = priorityAnnoOnProvider.value().asInt(); + } + } + return priority; + } + @BuildStep AdditionalBeanBuildItem registerProviderBeans(CombinedIndexBuildItem combinedIndex) { IndexView index = combinedIndex.getIndex(); diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/GlobalFeature.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/GlobalFeature.java new file mode 100644 index 0000000000000..09b39085a2f96 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/GlobalFeature.java @@ -0,0 +1,28 @@ +package io.quarkus.rest.client.reactive.provider; + +import javax.ws.rs.core.Feature; +import javax.ws.rs.core.FeatureContext; +import javax.ws.rs.ext.Provider; + +import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestContext; +import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestFilter; + +@Provider +public class GlobalFeature implements Feature { + + public static boolean called; + + @Override + public boolean configure(FeatureContext context) { + context.register(FeatureInstalledFilter.class); + return true; + } + + public static class FeatureInstalledFilter implements ResteasyReactiveClientRequestFilter { + + @Override + public void filter(ResteasyReactiveClientRequestContext requestContext) { + called = true; + } + } +} diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/GlobalRequestFilter.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/GlobalRequestFilter.java new file mode 100644 index 0000000000000..57f2a95cf1657 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/GlobalRequestFilter.java @@ -0,0 +1,21 @@ +package io.quarkus.rest.client.reactive.provider; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestContext; +import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestFilter; + +@Provider +public class GlobalRequestFilter implements ResteasyReactiveClientRequestFilter { + public static final int STATUS = 233; + + public static boolean abort = false; + + @Override + public void filter(ResteasyReactiveClientRequestContext requestContext) { + if (abort) { + requestContext.abortWith(Response.status(STATUS).build()); + } + } +} diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/GlobalRequestFilterConstrainedToServer.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/GlobalRequestFilterConstrainedToServer.java new file mode 100644 index 0000000000000..815a7c28f7155 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/GlobalRequestFilterConstrainedToServer.java @@ -0,0 +1,17 @@ +package io.quarkus.rest.client.reactive.provider; + +import javax.ws.rs.ConstrainedTo; +import javax.ws.rs.RuntimeType; +import javax.ws.rs.ext.Provider; + +import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestContext; +import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestFilter; + +@Provider +@ConstrainedTo(RuntimeType.SERVER) +public class GlobalRequestFilterConstrainedToServer implements ResteasyReactiveClientRequestFilter { + @Override + public void filter(ResteasyReactiveClientRequestContext requestContext) { + throw new RuntimeException("Invoked filter that is constrained to server"); + } +} diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/GlobalResponseFilter.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/GlobalResponseFilter.java new file mode 100644 index 0000000000000..b26bac87f264b --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/GlobalResponseFilter.java @@ -0,0 +1,22 @@ +package io.quarkus.rest.client.reactive.provider; + +import javax.annotation.Priority; +import javax.ws.rs.client.ClientResponseContext; +import javax.ws.rs.ext.Provider; + +import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestContext; +import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientResponseFilter; + +@Provider +@Priority(20) +public class GlobalResponseFilter implements ResteasyReactiveClientResponseFilter { + + public static final int STATUS = 222; + + @Override + public void filter(ResteasyReactiveClientRequestContext requestContext, ClientResponseContext responseContext) { + if (responseContext.getStatus() != GlobalRequestFilter.STATUS) { + responseContext.setStatus(STATUS); + } + } +} diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/GlobalResponseFilterLowPrio.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/GlobalResponseFilterLowPrio.java new file mode 100644 index 0000000000000..64fc0ae57d4ea --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/GlobalResponseFilterLowPrio.java @@ -0,0 +1,23 @@ +package io.quarkus.rest.client.reactive.provider; + +import javax.annotation.Priority; +import javax.ws.rs.client.ClientResponseContext; +import javax.ws.rs.ext.Provider; + +import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestContext; +import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientResponseFilter; + +@Priority(10) // lower prio here means executed later +@Provider +public class GlobalResponseFilterLowPrio implements ResteasyReactiveClientResponseFilter { + + public static final int STATUS = 244; + public static boolean skip = false; + + @Override + public void filter(ResteasyReactiveClientRequestContext requestContext, ClientResponseContext responseContext) { + if (!skip) { + responseContext.setStatus(STATUS); + } + } +} diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/HelloClient.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/HelloClient.java new file mode 100644 index 0000000000000..d94255de56976 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/HelloClient.java @@ -0,0 +1,19 @@ +package io.quarkus.rest.client.reactive.provider; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +@Path("/hello") +@Produces(MediaType.TEXT_PLAIN) +@Consumes(MediaType.TEXT_PLAIN) +@RegisterRestClient +public interface HelloClient { + @POST + Response echo(String name); +} diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/HelloClientWithFilter.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/HelloClientWithFilter.java new file mode 100644 index 0000000000000..6c4ab9f666425 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/HelloClientWithFilter.java @@ -0,0 +1,21 @@ +package io.quarkus.rest.client.reactive.provider; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +@Path("/hello") +@Produces(MediaType.TEXT_PLAIN) +@Consumes(MediaType.TEXT_PLAIN) +@RegisterProvider(ResponseFilterLowestPrio.class) +@RegisterRestClient +public interface HelloClientWithFilter { + @POST + Response echo(String name); +} diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ProviderDisabledAutodiscoveryTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ProviderDisabledAutodiscoveryTest.java new file mode 100644 index 0000000000000..b318c03b0398b --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ProviderDisabledAutodiscoveryTest.java @@ -0,0 +1,56 @@ +package io.quarkus.rest.client.reactive.provider; + +import static io.quarkus.rest.client.reactive.RestClientTestUtil.setUrlForClass; +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URI; + +import javax.ws.rs.core.Response; + +import org.eclipse.microprofile.rest.client.RestClientBuilder; +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.rest.client.reactive.HelloResource; +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; + +public class ProviderDisabledAutodiscoveryTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(HelloResource.class, HelloClient.class, GlobalRequestFilter.class, GlobalResponseFilter.class) + .addAsResource( + new StringAsset(setUrlForClass(HelloClient.class) + + "quarkus.rest-client-reactive.provider-autodiscovery=false"), + "application.properties")); + + @RestClient + HelloClient helloClient; + + @TestHTTPResource + URI baseUri; + + @Test + void shouldNotUseGlobalFilterForInjectedClient() { + Response response = helloClient.echo("Michał"); + assertThat(response.getStatus()).isEqualTo(200); + } + + @Test + void shouldNotUseGlobalFilterForBuiltClient() { + Response response = helloClient().echo("Michał"); + assertThat(response.getStatus()).isEqualTo(200); + } + + private HelloClient helloClient() { + return RestClientBuilder.newBuilder() + .baseUri(baseUri) + .build(HelloClient.class); + } +} diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ProviderPriorityTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ProviderPriorityTest.java new file mode 100644 index 0000000000000..45e8178d2dee6 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ProviderPriorityTest.java @@ -0,0 +1,64 @@ +package io.quarkus.rest.client.reactive.provider; + +import static io.quarkus.rest.client.reactive.RestClientTestUtil.setUrlForClass; +import static org.assertj.core.api.Assertions.assertThat; + +import javax.ws.rs.core.Response; + +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.rest.client.reactive.HelloResource; +import io.quarkus.test.QuarkusUnitTest; + +public class ProviderPriorityTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(HelloResource.class, + HelloClient.class, + HelloClientWithFilter.class, + ResponseFilterLowestPrio.class, + GlobalResponseFilter.class, + GlobalResponseFilterLowPrio.class) + .addAsResource( + new StringAsset(setUrlForClass(HelloClient.class) + + setUrlForClass(HelloClientWithFilter.class)), + "application.properties")); + + @RestClient + HelloClient helloClient; + + @RestClient + HelloClientWithFilter helloClientWithFilter; + + @AfterEach + void cleanUp() { + GlobalResponseFilterLowPrio.skip = false; + } + + @Test + void shouldApplyLocalLowestPrioFilterLast() { + Response response = helloClientWithFilter.echo("foo"); + assertThat(response.getStatus()).isEqualTo(ResponseFilterLowestPrio.STATUS); + } + + @Test + void shouldApplyLowPrioFilterLast() { + Response response = helloClient.echo("foo"); + assertThat(response.getStatus()).isEqualTo(GlobalResponseFilterLowPrio.STATUS); + } + + @Test + void shouldApplyHighPrioFilter() { + GlobalResponseFilterLowPrio.skip = true; + Response response = helloClient.echo("foo"); + assertThat(response.getStatus()).isEqualTo(GlobalResponseFilter.STATUS); + } +} diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ProviderTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ProviderTest.java new file mode 100644 index 0000000000000..897db05f57db9 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ProviderTest.java @@ -0,0 +1,85 @@ +package io.quarkus.rest.client.reactive.provider; + +import static io.quarkus.rest.client.reactive.RestClientTestUtil.setUrlForClass; +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URI; + +import javax.ws.rs.core.Response; + +import org.eclipse.microprofile.rest.client.RestClientBuilder; +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.rest.client.reactive.HelloResource; +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; + +public class ProviderTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(HelloResource.class, HelloClient.class, GlobalRequestFilter.class, + GlobalResponseFilter.class, GlobalRequestFilterConstrainedToServer.class, + GlobalFeature.class) + .addAsResource( + new StringAsset(setUrlForClass(HelloClient.class)), + "application.properties")); + + @RestClient + HelloClient helloClient; + + @TestHTTPResource + URI baseUri; + + @AfterEach + public void cleanUp() { + GlobalRequestFilter.abort = false; + GlobalFeature.called = false; + } + + @Test + void shouldNotRegisterFeatureAutomatically() { + Response response = helloClient.echo("Michał"); + assertThat(response.getStatus()).isEqualTo(GlobalResponseFilter.STATUS); + assertThat(GlobalFeature.called).isFalse(); + } + + @Test + void shouldUseGlobalRequestFilterForInjectedClient() { + GlobalRequestFilter.abort = true; + Response response = helloClient.echo("Michał"); + assertThat(response.getStatus()).isEqualTo(GlobalRequestFilter.STATUS); + } + + @Test + void shouldUseGlobalResponseFilterForInjectedClient() { + Response response = helloClient.echo("Michał"); + assertThat(response.getStatus()).isEqualTo(GlobalResponseFilter.STATUS); + } + + @Test + void shouldUseGlobalRequestFilterForBuiltClient() { + GlobalRequestFilter.abort = true; + Response response = helloClient().echo("Michał"); + assertThat(response.getStatus()).isEqualTo(GlobalRequestFilter.STATUS); + } + + @Test + void shouldUseGlobalResponseFilterForBuiltClient() { + Response response = helloClient().echo("Michał"); + assertThat(response.getStatus()).isEqualTo(GlobalResponseFilter.STATUS); + } + + private HelloClient helloClient() { + return RestClientBuilder.newBuilder() + .baseUri(baseUri) + .build(HelloClient.class); + } +} diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ResponseFilterLowestPrio.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ResponseFilterLowestPrio.java new file mode 100644 index 0000000000000..d29961b978803 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/provider/ResponseFilterLowestPrio.java @@ -0,0 +1,21 @@ +package io.quarkus.rest.client.reactive.provider; + +import javax.annotation.Priority; +import javax.ws.rs.client.ClientResponseContext; + +import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestContext; +import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientResponseFilter; + +@Priority(1) +public class ResponseFilterLowestPrio implements ResteasyReactiveClientResponseFilter { + + public static final int STATUS = 266; + public static boolean skip = false; + + @Override + public void filter(ResteasyReactiveClientRequestContext requestContext, ClientResponseContext responseContext) { + if (!skip) { + responseContext.setStatus(STATUS); + } + } +} diff --git a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/AnnotationRegisteredProviders.java b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/AnnotationRegisteredProviders.java index 72d50cebc9fd0..2dd034ae78793 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/AnnotationRegisteredProviders.java +++ b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/AnnotationRegisteredProviders.java @@ -1,19 +1,26 @@ package io.quarkus.rest.client.reactive.runtime; -import java.util.Collections; import java.util.HashMap; import java.util.Map; public abstract class AnnotationRegisteredProviders { private final Map, Integer>> providers = new HashMap<>(); + private final Map, Integer> globalProviders = new HashMap<>(); public Map, Integer> getProviders(Class clientClass) { - Map, Integer> providersForClass = providers.get(clientClass.getName()); - return providersForClass == null ? Collections.emptyMap() : providersForClass; + return providers.getOrDefault(clientClass.getName(), globalProviders); } // used by generated code + // MUST be called after addGlobalProvider public void addProviders(String className, Map, Integer> providersForClass) { - this.providers.put(className, providersForClass); + Map, Integer> providers = new HashMap<>(providersForClass); + providers.putAll(globalProviders); + this.providers.put(className, providers); + } + + // used by generated code + public void addGlobalProvider(Class providerClass, int priority) { + globalProviders.put(providerClass, priority); } } diff --git a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientReactiveConfig.java b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientReactiveConfig.java index 4dd42dd348918..ac649b8d48e66 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientReactiveConfig.java +++ b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientReactiveConfig.java @@ -22,4 +22,11 @@ public class RestClientReactiveConfig { */ @ConfigItem(name = "disable-smart-produces", defaultValue = "false") public boolean disableSmartProduces; + + /** + * Whether or not providers (filters, etc) annotated with {@link javax.ws.rs.ext.Provider} should be + * automatically registered for all the clients in the application. + */ + @ConfigItem(name = "provider-autodiscovery", defaultValue = "true") + public boolean providerAutodiscovery = true; } diff --git a/integration-tests/oidc-client-reactive/src/main/java/io/quarkus/it/keycloak/OidcClientRequestCustomFilter.java b/integration-tests/oidc-client-reactive/src/main/java/io/quarkus/it/keycloak/OidcClientRequestCustomFilter.java index 7babb97b07d7f..04147c6992e15 100644 --- a/integration-tests/oidc-client-reactive/src/main/java/io/quarkus/it/keycloak/OidcClientRequestCustomFilter.java +++ b/integration-tests/oidc-client-reactive/src/main/java/io/quarkus/it/keycloak/OidcClientRequestCustomFilter.java @@ -6,7 +6,6 @@ import javax.ws.rs.Priorities; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; -import javax.ws.rs.ext.Provider; import org.jboss.logging.Logger; import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestContext; @@ -17,7 +16,6 @@ import io.quarkus.oidc.client.runtime.DisabledOidcClientException; import io.quarkus.oidc.common.runtime.OidcConstants; -@Provider @Priority(Priorities.AUTHENTICATION) public class OidcClientRequestCustomFilter extends AbstractTokensProducer implements ResteasyReactiveClientRequestFilter { private static final Logger LOG = Logger.getLogger(OidcClientRequestCustomFilter.class); @@ -53,4 +51,4 @@ public void accept(Throwable t) { } }); } -} \ No newline at end of file +}