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

Rest Client Reactive: globally register providers annotated with @Provider #18957

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -24,8 +25,11 @@ EnableAllSecurityServicesBuildItem security() {

@BuildStep(onlyIf = IsEnabled.class)
void registerProvider(BuildProducer<AdditionalBeanBuildItem> additionalBeans,
BuildProducer<ReflectiveClassBuildItem> reflectiveClass) {
BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
BuildProducer<AdditionalIndexedClassesBuildItem> additionalIndexedClassesBuildItem) {
additionalBeans.produce(AdditionalBeanBuildItem.unremovableOf(OidcClientRequestReactiveFilter.class));
additionalIndexedClassesBuildItem
.produce(new AdditionalIndexedClassesBuildItem(OidcClientRequestReactiveFilter.class.getName()));
reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, OidcClientRequestReactiveFilter.class));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -178,17 +180,23 @@ 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:
* <ul>
* <li>puts all the providers registered by the @RegisterProvider annotation in a
* map using the {@link AnnotationRegisteredProviders#addProviders(String, Map)} method</li>
* <li>registers all the provider implementations annotated with @Provider using
* {@link AnnotationRegisteredProviders#addGlobalProvider(Class, int)}</li>
* </ul>
*
*
* @param indexBuildItem index
* @param generatedBeans build producer for generated beans
*/
@BuildStep
void registerProvidersFromAnnotations(CombinedIndexBuildItem indexBuildItem,
BuildProducer<GeneratedBeanBuildItem> generatedBeans,
BuildProducer<UnremovableBeanBuildItem> unremovableBeans) {
BuildProducer<UnremovableBeanBuildItem> unremovableBeans,
RestClientReactiveConfig clientConfig) {
String annotationRegisteredProvidersImpl = AnnotationRegisteredProviders.class.getName() + "Implementation";
IndexView index = indexBuildItem.getIndex();
Map<String, List<AnnotationInstance>> annotationsByClassName = new HashMap<>();
Expand Down Expand Up @@ -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<String, List<AnnotationInstance>> 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,
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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());
}
}
}
Original file line number Diff line number Diff line change
@@ -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");
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading