Skip to content

Commit

Permalink
QuarkusComponentTest: support priority declared on an interceptor method
Browse files Browse the repository at this point in the history
  • Loading branch information
mkouba committed Jun 22, 2023
1 parent 4726d3a commit d759d21
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class InterceptorMethodCreator implements InterceptorCreator {
public InterceptFunction create(SyntheticCreationalContext<Object> context) {
Object createKey = context.getParams().get(CREATE_KEY);
if (createKey != null) {
Function<SyntheticCreationalContext<?>, InterceptFunction> createFun = createFunctions.get(createKey.toString());
Function<SyntheticCreationalContext<?>, InterceptFunction> createFun = createFunctions.get(createKey);
if (createFun != null) {
return createFun.apply(context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class MockBeanCreator implements BeanCreator<Object> {
public Object create(SyntheticCreationalContext<Object> context) {
Object createKey = context.getParams().get(CREATE_KEY);
if (createKey != null) {
Function<SyntheticCreationalContext<?>, ?> createFun = createFunctions.get(createKey.toString());
Function<SyntheticCreationalContext<?>, ?> createFun = createFunctions.get(createKey);
if (createFun != null) {
return createFun.apply(context);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -453,8 +454,8 @@ private static Set<AnnotationInstance> getQualifiers(Field field, Collection<Dot
return ret;
}

private ClassLoader initArcContainer(ExtensionContext context, Collection<Class<?>> componentClasses) {
Class<?> testClass = context.getRequiredTestClass();
private ClassLoader initArcContainer(ExtensionContext extensionContext, Collection<Class<?>> componentClasses) {
Class<?> testClass = extensionContext.getRequiredTestClass();
// Collect all test class injection points to define a bean removal exclusion
List<Field> testClassInjectionPoints = findInjectFields(testClass);

Expand Down Expand Up @@ -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)));
Expand Down Expand Up @@ -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<Annotation> 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);
}
});

Expand Down Expand Up @@ -800,6 +755,62 @@ public void accept(BytecodeTransformer transformer) {
return oldTccl;
}

private void processTestInterceptorMethods(Class<?> testClass, ExtensionContext extensionContext,
BeanRegistrar.RegistrationContext registrationContext, Set<String> interceptorBindings) {
for (Method method : findMethods(testClass,
List.of(AroundInvoke.class, PostConstruct.class, PreDestroy.class, AroundConstruct.class))) {
Set<Annotation> 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())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit d759d21

Please sign in to comment.