Skip to content

Commit

Permalink
Merge pull request quarkusio#21824 from Ladicek/custom-qualifiers-at-…
Browse files Browse the repository at this point in the history
…runtime

Add support for custom qualifiers to runtime qualifier validation
  • Loading branch information
mkouba authored Dec 1, 2021
2 parents 68b9b03 + c9f330b commit 231d724
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,11 @@ public class BeanDeployment {
DotName dotName = entry.getKey();
ClassInfo classInfo = getClassByName(this.beanArchiveIndex, dotName);
if (classInfo != null) {
if (entry.getValue() != null) {
qualifierNonbindingMembers.put(dotName, entry.getValue());
Set<String> nonbindingMembers = entry.getValue();
if (nonbindingMembers == null) {
nonbindingMembers = Collections.emptySet();
}
qualifierNonbindingMembers.put(dotName, nonbindingMembers);
this.qualifiers.put(dotName, classInfo);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ private <T> InjectableBean<T> getBean(Type requiredType, Annotation... qualifier
if (qualifiers == null || qualifiers.length == 0) {
qualifiers = new Annotation[] { Default.Literal.INSTANCE };
} else {
Qualifiers.verify(qualifiers);
Qualifiers.verify(qualifiers, qualifierNonbindingMembers.keySet());
}
Set<InjectableBean<?>> resolvedBeans = resolved.getValue(new Resolvable(requiredType, qualifiers));
return resolvedBeans.size() != 1 ? null : (InjectableBean<T>) resolvedBeans.iterator().next();
Expand All @@ -468,7 +468,7 @@ Set<Bean<?>> getBeans(Type requiredType, Annotation... qualifiers) {
if (requiredType instanceof TypeVariable) {
throw new IllegalArgumentException("The given type is a type variable: " + requiredType);
}
Qualifiers.verify(qualifiers);
Qualifiers.verify(qualifiers, qualifierNonbindingMembers.keySet());
// This method does not cache the results
return Set.of(getMatchingBeans(new Resolvable(requiredType, qualifiers)).toArray(new Bean<?>[] {}));
}
Expand All @@ -482,6 +482,10 @@ Map<Class<? extends Annotation>, Set<Annotation>> getTransitiveInterceptorBindin
return transitiveInterceptorBindings;
}

Set<String> getCustomQualifiers() {
return qualifierNonbindingMembers.keySet();
}

boolean isScope(Class<? extends Annotation> annotationType) {
if (annotationType.isAnnotationPresent(Scope.class) || annotationType.isAnnotationPresent(NormalScope.class)) {
return true;
Expand Down Expand Up @@ -698,7 +702,7 @@ private static int compareAlternativeBeans(InjectableBean<?> bean1, InjectableBe

@SuppressWarnings("unchecked")
<T> List<InjectableObserverMethod<? super T>> resolveObservers(Type eventType, Set<Annotation> eventQualifiers) {
Qualifiers.verify(eventQualifiers);
Qualifiers.verify(eventQualifiers, qualifierNonbindingMembers.keySet());
if (observers.isEmpty()) {
return Collections.emptyList();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ public boolean isPassivatingScope(Class<? extends Annotation> annotationType) {

@Override
public boolean isQualifier(Class<? extends Annotation> annotationType) {
// BeforeBeanDiscovery.addQualifier() and equivalents are not supported
return annotationType.isAnnotationPresent(Qualifier.class);
return annotationType.isAnnotationPresent(Qualifier.class)
|| ArcContainerImpl.instance().getCustomQualifiers().contains(annotationType.getName());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,23 +118,23 @@ private Notifier<? super T> getNotifier(Class<?> runtimeType) {

@Override
public Event<T> select(Annotation... qualifiers) {
Qualifiers.verify(qualifiers);
Qualifiers.verify(qualifiers, ArcContainerImpl.instance().getCustomQualifiers());
Set<Annotation> mergedQualifiers = new HashSet<>(this.qualifiers);
Collections.addAll(mergedQualifiers, qualifiers);
return new EventImpl<T>(eventType, mergedQualifiers);
}

@Override
public <U extends T> Event<U> select(Class<U> subtype, Annotation... qualifiers) {
Qualifiers.verify(qualifiers);
Qualifiers.verify(qualifiers, ArcContainerImpl.instance().getCustomQualifiers());
Set<Annotation> mergerdQualifiers = new HashSet<>(this.qualifiers);
Collections.addAll(mergerdQualifiers, qualifiers);
return new EventImpl<U>(subtype, mergerdQualifiers);
}

@Override
public <U extends T> Event<U> select(TypeLiteral<U> subtype, Annotation... qualifiers) {
Qualifiers.verify(qualifiers);
Qualifiers.verify(qualifiers, ArcContainerImpl.instance().getCustomQualifiers());
if (Types.containsTypeVariable(subtype.getType())) {
throw new IllegalArgumentException(
"Event#select(TypeLiteral, Annotation...) cannot be used with type variable parameter");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,34 @@ public final class Qualifiers {
private Qualifiers() {
}

static void verify(Collection<Annotation> qualifiers) {
static void verify(Collection<Annotation> qualifiers, Set<String> customQualifiers) {
if (qualifiers.isEmpty()) {
return;
}

if (qualifiers.size() == 1) {
verifyQualifier(qualifiers.iterator().next().annotationType());
verifyQualifier(qualifiers.iterator().next().annotationType(), customQualifiers);
} else {
Map<Class<? extends Annotation>, Integer> timesQualifierWasSeen = new HashMap<>();
for (Annotation qualifier : qualifiers) {
verifyQualifier(qualifier.annotationType());
verifyQualifier(qualifier.annotationType(), customQualifiers);
timesQualifierWasSeen.compute(qualifier.annotationType(), TimesSeenBiFunction.INSTANCE);
}
checkQualifiersForDuplicates(timesQualifierWasSeen);
}
}

static void verify(Annotation... qualifiers) {
static void verify(Annotation[] qualifiers, Set<String> customQualifiers) {
if (qualifiers.length == 0) {
return;
}

if (qualifiers.length == 1) {
verifyQualifier(qualifiers[0].annotationType());
verifyQualifier(qualifiers[0].annotationType(), customQualifiers);
} else {
Map<Class<? extends Annotation>, Integer> timesQualifierWasSeen = new HashMap<>();
for (Annotation qualifier : qualifiers) {
verifyQualifier(qualifier.annotationType());
verifyQualifier(qualifier.annotationType(), customQualifiers);
timesQualifierWasSeen.compute(qualifier.annotationType(), TimesSeenBiFunction.INSTANCE);
}
checkQualifiersForDuplicates(timesQualifierWasSeen);
Expand Down Expand Up @@ -145,7 +145,11 @@ private static Object invoke(Method method, Object instance) {
}
}

private static void verifyQualifier(Class<? extends Annotation> annotationType) {
private static void verifyQualifier(Class<? extends Annotation> annotationType, Set<String> customQualifiers) {
if (customQualifiers.contains(annotationType.getName())) {
return;
}

if (!annotationType.isAnnotationPresent(Qualifier.class)) {
throw new IllegalArgumentException("Annotation is not a qualifier: " + annotationType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import io.quarkus.arc.Arc;
import io.quarkus.arc.ManagedContext;
import io.quarkus.arc.processor.QualifierRegistrar;
import io.quarkus.arc.test.ArcTestContainer;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
Expand All @@ -22,6 +23,7 @@
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
Expand Down Expand Up @@ -51,15 +53,24 @@
import javax.interceptor.Interceptor;
import javax.interceptor.InterceptorBinding;
import javax.interceptor.InvocationContext;
import org.jboss.jandex.DotName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

public class BeanManagerTest {

@RegisterExtension
public ArcTestContainer container = new ArcTestContainer(Legacy.class, AlternativeLegacy.class, Fool.class,
DummyInterceptor.class, DummyBinding.class, LowPriorityInterceptor.class, WithInjectionPointMetadata.class,
High.class, Observers.class);
public ArcTestContainer container = new ArcTestContainer.Builder()
.beanClasses(Legacy.class, AlternativeLegacy.class, Fool.class, DummyInterceptor.class, DummyBinding.class,
LowPriorityInterceptor.class, WithInjectionPointMetadata.class, High.class, Low.class, Observers.class,
BeanWithCustomQualifier.class)
.qualifierRegistrars(new QualifierRegistrar() {
@Override
public Map<DotName, Set<String>> getAdditionalQualifiers() {
return Map.of(DotName.createSimple(Low.class.getName()), Set.of());
}
})
.build();

@Test
public void testGetBeans() {
Expand Down Expand Up @@ -172,6 +183,7 @@ public void testIsQualifier() {
BeanManager beanManager = Arc.container().beanManager();
assertTrue(beanManager.isQualifier(Default.class));
assertTrue(beanManager.isQualifier(High.class));
assertTrue(beanManager.isQualifier(Low.class));
assertFalse(beanManager.isQualifier(ApplicationScoped.class));
}

Expand Down Expand Up @@ -200,19 +212,39 @@ public void testResolveObservers() {
assertEquals(Number.class, observers.iterator().next().getObservedType());
}

@Test
public void testGetBeanWithCustomQualifier() {
BeanManager beanManager = Arc.container().beanManager();
Set<Bean<?>> beans = beanManager.getBeans(BeanWithCustomQualifier.class, Low.Literal.INSTANCE);
assertEquals(1, beans.size());
}

@ApplicationScoped
static class Observers {

void observe(@Observes @High Number number) {
}
}

@ApplicationScoped
@Low
static class BeanWithCustomQualifier {
}

@Target({ TYPE, METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
@Documented
@Qualifier
public @interface High {
}

@Target({ TYPE, METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Low {
class Literal extends AnnotationLiteral<Low> implements Low {
public static final Literal INSTANCE = new Literal();
}
}

@Dependent
Expand Down

0 comments on commit 231d724

Please sign in to comment.