Skip to content

Commit

Permalink
Rest Client Reactive: globally register providers annotated with @Pro…
Browse files Browse the repository at this point in the history
  • Loading branch information
michalszynkiewicz committed Jul 23, 2021
1 parent 8df4cbb commit 4c717be
Show file tree
Hide file tree
Showing 10 changed files with 319 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package io.quarkus.rest.client.reactive.deployment;

import static io.quarkus.arc.processor.MethodDescriptors.MAP_PUT;
import static io.quarkus.rest.client.reactive.deployment.DotNames.REGISTER_CLIENT_HEADERS;
import static io.quarkus.rest.client.reactive.deployment.DotNames.REGISTER_PROVIDER;
import static io.quarkus.rest.client.reactive.deployment.DotNames.REGISTER_PROVIDERS;
import static io.quarkus.rest.client.reactive.deployment.DotNames.*;
import static org.jboss.resteasy.reactive.common.processor.EndpointIndexer.CDI_WRAPPER_SUFFIX;
import static org.jboss.resteasy.reactive.common.processor.scanning.ResteasyReactiveScanner.BUILTIN_HTTP_ANNOTATIONS_TO_METHOD;

Expand All @@ -22,22 +20,15 @@
import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.Typed;
import javax.inject.Singleton;
import javax.ws.rs.Priorities;
import javax.ws.rs.core.MediaType;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.rest.client.ext.QueryParamStyle;
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.AnnotationValue;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.CompositeIndex;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.Type;
import org.jboss.jandex.*;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames;

Expand Down Expand Up @@ -178,9 +169,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:
* <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
Expand Down Expand Up @@ -217,14 +213,34 @@ void registerProvidersFromAnnotations(CombinedIndexBuildItem indexBuildItem,
constructor.invokeSpecialMethod(MethodDescriptor.ofConstructor(AnnotationRegisteredProviders.class),
constructor.getThis());

for (AnnotationInstance instance : index.getAnnotations(ResteasyReactiveDotNames.PROVIDER)) {
// TODO: we may want to filter out stuff that is server side only
ClassInfo providerClass = instance.target().asClass();

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 +254,15 @@ 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));
AnnotationInstance priorityAnnoOnProvider = providerClass.classAnnotation(ResteasyReactiveDotNames.PRIORITY);
if (priorityAnnoOnProvider != null) {
defaultPriority = priorityAnnoOnProvider.value().asInt();
}
return defaultPriority;
}

@BuildStep
AdditionalBeanBuildItem registerProviderBeans(CombinedIndexBuildItem combinedIndex) {
IndexView index = combinedIndex.getIndex();
Expand Down
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,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,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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
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)
.addAsResource(
new StringAsset(setUrlForClass(HelloClient.class)),
"application.properties"));

@RestClient
HelloClient helloClient;

@TestHTTPResource
URI baseUri;

@AfterEach
public void cleanUp() {
GlobalRequestFilter.abort = false;
}

@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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Loading

0 comments on commit 4c717be

Please sign in to comment.