Skip to content

Commit

Permalink
Merge pull request #20699 from mkouba/issue-20615
Browse files Browse the repository at this point in the history
RESTEasy classic rest client - make associated interceptors unremovable
  • Loading branch information
geoand authored Oct 13, 2021
2 parents 51a747b + 7f53fe9 commit 26b02a9
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.stream.Collectors;

import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.spi.InterceptionType;
import javax.ws.rs.Path;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.ClientResponseFilter;
Expand All @@ -29,6 +30,7 @@
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationTarget.Kind;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.CompositeIndex;
Expand All @@ -50,10 +52,12 @@
import io.quarkus.arc.BeanDestroyer;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem;
import io.quarkus.arc.deployment.InterceptorResolverBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem.ExtendedBeanConfigurator;
import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
import io.quarkus.arc.processor.BuiltinScope;
import io.quarkus.arc.processor.InterceptorInfo;
import io.quarkus.arc.processor.ScopeInfo;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
Expand Down Expand Up @@ -516,6 +520,57 @@ AdditionalBeanBuildItem registerProviderBeans(CombinedIndexBuildItem combinedInd
return builder.build();
}

@BuildStep
void unremovableInterceptors(List<RestClientBuildItem> restClientInterfaces, BeanArchiveIndexBuildItem beanArchiveIndex,
InterceptorResolverBuildItem interceptorResolver, BuildProducer<UnremovableBeanBuildItem> unremovableBeans) {

if (restClientInterfaces.isEmpty()) {
return;
}

IndexView index = beanArchiveIndex.getIndex();
Set<DotName> interceptorBindings = interceptorResolver.getInterceptorBindings();
Set<String> unremovableInterceptors = new HashSet<>();

for (RestClientBuildItem restClient : restClientInterfaces) {
ClassInfo restClientClass = index.getClassByName(DotName.createSimple(restClient.getInterfaceName()));
if (restClientClass != null) {
Set<AnnotationInstance> classLevelBindings = new HashSet<>();
for (AnnotationInstance annotationInstance : restClientClass.classAnnotations()) {
if (interceptorBindings.contains(annotationInstance.name())) {
classLevelBindings.add(annotationInstance);
}
}
for (MethodInfo method : restClientClass.methods()) {
if (Modifier.isStatic(method.flags())) {
continue;
}
Set<AnnotationInstance> bindings = new HashSet<>(classLevelBindings);
for (AnnotationInstance annotationInstance : method.annotations()) {
if (annotationInstance.target().kind() == Kind.METHOD
&& interceptorBindings.contains(annotationInstance.name())) {
bindings.add(annotationInstance);
}
}
if (bindings.isEmpty()) {
continue;
}
List<InterceptorInfo> interceptors = interceptorResolver.get().resolve(
InterceptionType.AROUND_INVOKE,
bindings);
if (!interceptors.isEmpty()) {
interceptors.stream().map(InterceptorInfo::getBeanClass).map(Object::toString)
.forEach(unremovableInterceptors::add);
}
}
}
}
if (!unremovableInterceptors.isEmpty()) {
unremovableBeans.produce(UnremovableBeanBuildItem.beanClassNames(unremovableInterceptors));
}

}

private boolean isRestClientInterface(IndexView index, ClassInfo classInfo) {
return Modifier.isInterface(classInfo.flags())
&& index.getAllKnownImplementors(classInfo.name()).isEmpty();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package io.quarkus.restclient.interceptor;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.net.URL;

import javax.annotation.Priority;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InterceptorBinding;
import javax.interceptor.InvocationContext;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;

import org.eclipse.microprofile.rest.client.RestClientBuilder;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.common.http.TestHTTPResource;

public class RestClientInterceptorTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(RestClientInterceptorTest.class, Client.class, Foo.class, FooInterceptor.class));

@TestHTTPResource
URL url;

@Test
public void testInterception() {
Client client = RestClientBuilder.newBuilder().baseUrl(url).build(Client.class);
assertEquals("foo", client.ping());
}

@RegisterRestClient
public interface Client {

@Foo
@GET
@Path("/test")
String ping();

}

@Path("/test")
public static class TestEndpoint {

@GET
public String get() {
throw new WebApplicationException(404);
}

}

@Target({ TYPE, METHOD })
@Retention(RUNTIME)
@Documented
@InterceptorBinding
public @interface Foo {

}

@Foo
@Priority(1)
@Interceptor
public static class FooInterceptor {

@AroundInvoke
Object aroundInvoke(InvocationContext ctx) {
return "foo";
}

}
}

0 comments on commit 26b02a9

Please sign in to comment.