From d759d21c67f904306e5cc7b1bc6bcf56be9af04d Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Thu, 22 Jun 2023 11:43:30 +0200 Subject: [PATCH] QuarkusComponentTest: support priority declared on an interceptor method --- .../component/InterceptorMethodCreator.java | 2 +- .../test/component/MockBeanCreator.java | 2 +- .../QuarkusComponentTestExtension.java | 111 ++++++++++-------- .../declarative/InterceptorMethodsTest.java | 22 +++- 4 files changed, 80 insertions(+), 57 deletions(-) diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/InterceptorMethodCreator.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/InterceptorMethodCreator.java index 9f0f09b91035c..b82442f1a6e56 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/InterceptorMethodCreator.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/InterceptorMethodCreator.java @@ -17,7 +17,7 @@ public class InterceptorMethodCreator implements InterceptorCreator { public InterceptFunction create(SyntheticCreationalContext context) { Object createKey = context.getParams().get(CREATE_KEY); if (createKey != null) { - Function, InterceptFunction> createFun = createFunctions.get(createKey.toString()); + Function, InterceptFunction> createFun = createFunctions.get(createKey); if (createFun != null) { return createFun.apply(context); } diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/MockBeanCreator.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/MockBeanCreator.java index 45fcc444509d9..4b888a3d74945 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/MockBeanCreator.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/MockBeanCreator.java @@ -22,7 +22,7 @@ public class MockBeanCreator implements BeanCreator { public Object create(SyntheticCreationalContext context) { Object createKey = context.getParams().get(CREATE_KEY); if (createKey != null) { - Function, ?> createFun = createFunctions.get(createKey.toString()); + Function, ?> createFun = createFunctions.get(createKey); if (createFun != null) { return createFun.apply(context); } else { diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java index 8734595023487..c4e1c642ac853 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java @@ -34,6 +34,7 @@ import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; +import jakarta.annotation.Priority; import jakarta.enterprise.context.Dependent; import jakarta.enterprise.event.Event; import jakarta.enterprise.inject.Instance; @@ -453,8 +454,8 @@ private static Set getQualifiers(Field field, Collection> componentClasses) { - Class testClass = context.getRequiredTestClass(); + private ClassLoader initArcContainer(ExtensionContext extensionContext, Collection> componentClasses) { + Class testClass = extensionContext.getRequiredTestClass(); // Collect all test class injection points to define a bean removal exclusion List testClassInjectionPoints = findInjectFields(testClass); @@ -584,7 +585,7 @@ public void writeResource(Resource resource) throws IOException { }); } - context.getRoot().getStore(NAMESPACE).put(KEY_GENERATED_RESOURCES, generatedResources); + extensionContext.getRoot().getStore(NAMESPACE).put(KEY_GENERATED_RESOURCES, generatedResources); builder.addAnnotationTransformer(AnnotationsTransformer.appliedToField().whenContainsAny(qualifiers) .whenContainsNone(DotName.createSimple(Inject.class)).thenTransform(t -> t.add(Inject.class))); @@ -695,53 +696,7 @@ public void register(RegistrationContext registrationContext) { unsatisfiedInjectionPoints.size()); // Find all methods annotated with interceptor annotations and register them as synthetic interceptors - for (Method method : findMethods(testClass, - List.of(AroundInvoke.class, PostConstruct.class, PreDestroy.class, AroundConstruct.class))) { - Set bindings = findBindings(method, interceptorBindings); - if (bindings.isEmpty()) { - throw new IllegalStateException("No bindings declared on a test interceptor method: " + method); - } - validateTestInterceptorMethod(method); - String key = UUID.randomUUID().toString(); - InterceptorMethodCreator.registerCreate(key, ctx -> { - return ic -> { - Object instance = null; - if (!Modifier.isStatic(method.getModifiers())) { - // ExtentionContext.getTestInstance() does not work - Object testInstance = context.getRoot().getStore(NAMESPACE).get(KEY_TEST_INSTANCE, - Object.class); - if (testInstance == null) { - throw new IllegalStateException("Test instance not available"); - } - instance = testInstance; - if (!method.canAccess(instance)) { - method.setAccessible(true); - } - } - return method.invoke(instance, ic); - }; - }); - InterceptionType interceptionType; - if (method.isAnnotationPresent(AroundInvoke.class)) { - interceptionType = InterceptionType.AROUND_INVOKE; - } else if (method.isAnnotationPresent(PostConstruct.class)) { - interceptionType = InterceptionType.POST_CONSTRUCT; - } else if (method.isAnnotationPresent(PreDestroy.class)) { - interceptionType = InterceptionType.PRE_DESTROY; - } else if (method.isAnnotationPresent(AroundConstruct.class)) { - interceptionType = InterceptionType.AROUND_CONSTRUCT; - } else { - // This should never happen - throw new IllegalStateException("No interceptor annotation declared on: " + method); - } - registrationContext.configureInterceptor(interceptionType) - .identifier(key) - .bindings(bindings.stream().map(Annotations::jandexAnnotation) - .toArray(AnnotationInstance[]::new)) - .param(InterceptorMethodCreator.CREATE_KEY, key) - .creator(InterceptorMethodCreator.class); - ; - } + processTestInterceptorMethods(testClass, extensionContext, registrationContext, interceptorBindings); } }); @@ -800,6 +755,62 @@ public void accept(BytecodeTransformer transformer) { return oldTccl; } + private void processTestInterceptorMethods(Class testClass, ExtensionContext extensionContext, + BeanRegistrar.RegistrationContext registrationContext, Set interceptorBindings) { + for (Method method : findMethods(testClass, + List.of(AroundInvoke.class, PostConstruct.class, PreDestroy.class, AroundConstruct.class))) { + Set bindings = findBindings(method, interceptorBindings); + if (bindings.isEmpty()) { + throw new IllegalStateException("No bindings declared on a test interceptor method: " + method); + } + validateTestInterceptorMethod(method); + String key = UUID.randomUUID().toString(); + InterceptorMethodCreator.registerCreate(key, ctx -> { + return ic -> { + Object instance = null; + if (!Modifier.isStatic(method.getModifiers())) { + // ExtentionContext.getTestInstance() does not work + Object testInstance = extensionContext.getRoot().getStore(NAMESPACE).get(KEY_TEST_INSTANCE, + Object.class); + if (testInstance == null) { + throw new IllegalStateException("Test instance not available"); + } + instance = testInstance; + if (!method.canAccess(instance)) { + method.setAccessible(true); + } + } + return method.invoke(instance, ic); + }; + }); + InterceptionType interceptionType; + if (method.isAnnotationPresent(AroundInvoke.class)) { + interceptionType = InterceptionType.AROUND_INVOKE; + } else if (method.isAnnotationPresent(PostConstruct.class)) { + interceptionType = InterceptionType.POST_CONSTRUCT; + } else if (method.isAnnotationPresent(PreDestroy.class)) { + interceptionType = InterceptionType.PRE_DESTROY; + } else if (method.isAnnotationPresent(AroundConstruct.class)) { + interceptionType = InterceptionType.AROUND_CONSTRUCT; + } else { + // This should never happen + throw new IllegalStateException("No interceptor annotation declared on: " + method); + } + int priority = 1; + Priority priorityAnnotation = method.getAnnotation(Priority.class); + if (priorityAnnotation != null) { + priority = priorityAnnotation.value(); + } + registrationContext.configureInterceptor(interceptionType) + .identifier(key) + .priority(priority) + .bindings(bindings.stream().map(Annotations::jandexAnnotation) + .toArray(AnnotationInstance[]::new)) + .param(InterceptorMethodCreator.CREATE_KEY, key) + .creator(InterceptorMethodCreator.class); + } + } + private void validateTestInterceptorMethod(Method method) { Parameter[] params = method.getParameters(); if (params.length != 1 || !InvocationContext.class.isAssignableFrom(params[0].getType())) { diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/declarative/InterceptorMethodsTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/declarative/InterceptorMethodsTest.java index c1992b27434b0..c74fc86e3cba6 100644 --- a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/declarative/InterceptorMethodsTest.java +++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/declarative/InterceptorMethodsTest.java @@ -12,6 +12,7 @@ import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; +import jakarta.annotation.Priority; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.spi.Bean; import jakarta.inject.Inject; @@ -46,12 +47,14 @@ public class InterceptorMethodsTest { public void testPing() { EVENTS.clear(); Mockito.when(charlie.ping()).thenReturn("ok"); - assertEquals("ok", theComponent.ping()); + assertEquals("OK", theComponent.ping()); Arc.container().getActiveContext(ApplicationScoped.class).destroy(theComponent.getBean()); + assertEquals(5, EVENTS.size()); assertEquals("ac", EVENTS.get(0)); assertEquals("pc", EVENTS.get(1)); - assertEquals("ai", EVENTS.get(2)); - assertEquals("pd", EVENTS.get(3)); + assertEquals("ai2", EVENTS.get(2)); + assertEquals("ai1", EVENTS.get(3)); + assertEquals("pd", EVENTS.get(4)); } @SimpleBinding @@ -79,13 +82,22 @@ public Bean getBean() { } + @Priority(20) @SimpleBinding @AroundInvoke - Object aroundInvoke(InvocationContext context) throws Exception { - EVENTS.add("ai"); + Object aroundInvoke1(InvocationContext context) throws Exception { + EVENTS.add("ai1"); return Boolean.parseBoolean(context.proceed().toString()) ? charlie.ping() : "false"; } + // default priority is 1 + @SimpleBinding + @AroundInvoke + Object aroundInvoke2(InvocationContext context) throws Exception { + EVENTS.add("ai2"); + return context.proceed().toString().toUpperCase(); + } + @SimpleBinding @PostConstruct void postConstruct(ArcInvocationContext context) throws Exception {