From 1f270022b3a9aa634d26d3b84aa05a19e445e568 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Thu, 20 Apr 2023 17:05:24 +0200 Subject: [PATCH] ArC - support interception of static methods with repeatable bindings - related to #32786 --- .../InterceptorResolverBuildItem.java | 17 +++- .../InterceptedStaticMethodsProcessor.java | 15 ++-- .../RepeatingBindingStaticMethodTest.java | 85 +++++++++++++++++++ .../quarkus/arc/processor/BeanDeployment.java | 8 +- 4 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/staticmethods/RepeatingBindingStaticMethodTest.java diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/InterceptorResolverBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/InterceptorResolverBuildItem.java index b9d0770a10f58..abfd5c917a9c6 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/InterceptorResolverBuildItem.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/InterceptorResolverBuildItem.java @@ -1,9 +1,11 @@ package io.quarkus.arc.deployment; +import java.util.Collection; import java.util.Collections; import java.util.Set; import java.util.stream.Collectors; +import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; @@ -17,13 +19,14 @@ public final class InterceptorResolverBuildItem extends SimpleBuildItem { private final InterceptorResolver resolver; - private final Set interceptorBindings; + private final BeanDeployment beanDeployment; InterceptorResolverBuildItem(BeanDeployment beanDeployment) { this.resolver = beanDeployment.getInterceptorResolver(); this.interceptorBindings = Collections.unmodifiableSet( - beanDeployment.getInterceptorBindings().stream().map(ClassInfo::name).collect(Collectors.toSet())); + beanDeployment.getInterceptorBindings().stream().map(ClassInfo::name).collect(Collectors.toUnmodifiableSet())); + this.beanDeployment = beanDeployment; } public InterceptorResolver get() { @@ -38,4 +41,14 @@ public Set getInterceptorBindings() { return interceptorBindings; } + /** + * + * @param annotation + * @return the collection of interceptor bindings + * @see BeanDeployment#extractInterceptorBindings(AnnotationInstance) + */ + public Collection extractInterceptorBindings(AnnotationInstance annotation) { + return beanDeployment.extractInterceptorBindings(annotation); + } + } diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/staticmethods/InterceptedStaticMethodsProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/staticmethods/InterceptedStaticMethodsProcessor.java index 568036a05db26..6e5f32ac07e65 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/staticmethods/InterceptedStaticMethodsProcessor.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/staticmethods/InterceptedStaticMethodsProcessor.java @@ -87,9 +87,7 @@ void collectInterceptedStaticMethods(BeanArchiveIndexBuildItem beanArchiveIndex, BuildProducer interceptedStaticMethods, InterceptorResolverBuildItem interceptorResolver, TransformedAnnotationsBuildItem transformedAnnotations, BuildProducer unremovableBeans) { - // In this step we collect all intercepted static methods, i.e. static methods annotated with interceptor bindings - Set interceptorBindings = interceptorResolver.getInterceptorBindings(); for (ClassInfo clazz : beanArchiveIndex.getIndex().getKnownClasses()) { for (MethodInfo method : clazz.methods()) { @@ -107,12 +105,15 @@ void collectInterceptedStaticMethods(BeanArchiveIndexBuildItem beanArchiveIndex, // Only method-level bindings are considered due to backwards compatibility Set methodLevelBindings = null; for (AnnotationInstance annotationInstance : annotations) { - if (annotationInstance.target().kind() == Kind.METHOD - && interceptorBindings.contains(annotationInstance.name())) { - if (methodLevelBindings == null) { - methodLevelBindings = new HashSet<>(); + if (annotationInstance.target().kind() == Kind.METHOD) { + Collection bindings = interceptorResolver + .extractInterceptorBindings(annotationInstance); + if (!bindings.isEmpty()) { + if (methodLevelBindings == null) { + methodLevelBindings = new HashSet<>(); + } + methodLevelBindings.addAll(bindings); } - methodLevelBindings.add(annotationInstance); } } if (methodLevelBindings == null || methodLevelBindings.isEmpty()) { diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/staticmethods/RepeatingBindingStaticMethodTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/staticmethods/RepeatingBindingStaticMethodTest.java new file mode 100644 index 0000000000000..54219d0cf4a2e --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/staticmethods/RepeatingBindingStaticMethodTest.java @@ -0,0 +1,85 @@ +package io.quarkus.arc.test.interceptor.staticmethods; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +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.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class RepeatingBindingStaticMethodTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(root -> root + .addClasses(InterceptMe.class, SimpleBean.class, InterceptMeAlpha.class, InterceptMeBravo.class)); + + @Test + public void testInterceptor() { + assertEquals("a/b/PONG/b/a", SimpleBean.ping("pong")); + } + + public static class SimpleBean { + + @InterceptMe("alpha") + @InterceptMe("bravo") + public static String ping(String val) { + return val.toUpperCase(); + } + } + + @Priority(1) + @Interceptor + @InterceptMe("alpha") + static class InterceptMeAlpha { + + @AroundInvoke + Object aroundInvoke(InvocationContext ctx) throws Exception { + return "a/" + ctx.proceed() + "/a"; + } + + } + + @Priority(2) + @Interceptor + @InterceptMe("bravo") + static class InterceptMeBravo { + + @AroundInvoke + Object aroundInvoke(InvocationContext ctx) throws Exception { + return "b/" + ctx.proceed() + "/b"; + } + + } + + @Repeatable(InterceptMe.List.class) + @InterceptorBinding + @Target({ TYPE, METHOD, CONSTRUCTOR }) + @Retention(RUNTIME) + @interface InterceptMe { + + String value(); + + @Target({ TYPE, METHOD, CONSTRUCTOR }) + @Retention(RUNTIME) + @interface List { + InterceptMe[] value(); + } + + } + +} diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java index 215ce1e4c4e08..7e9457dd05743 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java @@ -511,6 +511,12 @@ Map> getQualifierNonbindingMembers() { return qualifierNonbindingMembers; } + /** + * + * @return the collection of interceptor bindings; the container annotations of repeating interceptor binding are not + * included + * @see #extractInterceptorBindings(AnnotationInstance) + */ public Collection getInterceptorBindings() { return Collections.unmodifiableCollection(interceptorBindings.values()); } @@ -614,7 +620,7 @@ Collection extractQualifiers(AnnotationInstance annotation) * @param annotation annotation to be inspected * @return a collection of interceptor bindings or an empty collection */ - Collection extractInterceptorBindings(AnnotationInstance annotation) { + public Collection extractInterceptorBindings(AnnotationInstance annotation) { return extractAnnotations(annotation, interceptorBindings, repeatingInterceptorBindingAnnotations); }