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 85d46c999b000..f797243500db4 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 @@ -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 nonbindingMembers = entry.getValue(); + if (nonbindingMembers == null) { + nonbindingMembers = Collections.emptySet(); } + qualifierNonbindingMembers.put(dotName, nonbindingMembers); this.qualifiers.put(dotName, classInfo); } } diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java index 6e108f4761182..63031ed8c8a05 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java @@ -458,7 +458,7 @@ private InjectableBean 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> resolvedBeans = resolved.getValue(new Resolvable(requiredType, qualifiers)); return resolvedBeans.size() != 1 ? null : (InjectableBean) resolvedBeans.iterator().next(); @@ -468,7 +468,7 @@ Set> 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[] {})); } @@ -482,6 +482,10 @@ Map, Set> getTransitiveInterceptorBindin return transitiveInterceptorBindings; } + Set getCustomQualifiers() { + return qualifierNonbindingMembers.keySet(); + } + boolean isScope(Class annotationType) { if (annotationType.isAnnotationPresent(Scope.class) || annotationType.isAnnotationPresent(NormalScope.class)) { return true; @@ -698,7 +702,7 @@ private static int compareAlternativeBeans(InjectableBean bean1, InjectableBe @SuppressWarnings("unchecked") List> resolveObservers(Type eventType, Set eventQualifiers) { - Qualifiers.verify(eventQualifiers); + Qualifiers.verify(eventQualifiers, qualifierNonbindingMembers.keySet()); if (observers.isEmpty()) { return Collections.emptyList(); } diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanManagerImpl.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanManagerImpl.java index beaa0d46e8df8..2d20797fa1169 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanManagerImpl.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanManagerImpl.java @@ -161,8 +161,8 @@ public boolean isPassivatingScope(Class annotationType) { @Override public boolean isQualifier(Class 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 diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/EventImpl.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/EventImpl.java index 8118d8d64658f..a5452e7f55215 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/EventImpl.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/EventImpl.java @@ -118,7 +118,7 @@ private Notifier getNotifier(Class runtimeType) { @Override public Event select(Annotation... qualifiers) { - Qualifiers.verify(qualifiers); + Qualifiers.verify(qualifiers, ArcContainerImpl.instance().getCustomQualifiers()); Set mergedQualifiers = new HashSet<>(this.qualifiers); Collections.addAll(mergedQualifiers, qualifiers); return new EventImpl(eventType, mergedQualifiers); @@ -126,7 +126,7 @@ public Event select(Annotation... qualifiers) { @Override public Event select(Class subtype, Annotation... qualifiers) { - Qualifiers.verify(qualifiers); + Qualifiers.verify(qualifiers, ArcContainerImpl.instance().getCustomQualifiers()); Set mergerdQualifiers = new HashSet<>(this.qualifiers); Collections.addAll(mergerdQualifiers, qualifiers); return new EventImpl(subtype, mergerdQualifiers); @@ -134,7 +134,7 @@ public Event select(Class subtype, Annotation... qualifiers) @Override public Event select(TypeLiteral 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"); diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/Qualifiers.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/Qualifiers.java index 19a54c417273b..44292fae65085 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/Qualifiers.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/Qualifiers.java @@ -26,34 +26,34 @@ public final class Qualifiers { private Qualifiers() { } - static void verify(Collection qualifiers) { + static void verify(Collection qualifiers, Set customQualifiers) { if (qualifiers.isEmpty()) { return; } if (qualifiers.size() == 1) { - verifyQualifier(qualifiers.iterator().next().annotationType()); + verifyQualifier(qualifiers.iterator().next().annotationType(), customQualifiers); } else { Map, 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 customQualifiers) { if (qualifiers.length == 0) { return; } if (qualifiers.length == 1) { - verifyQualifier(qualifiers[0].annotationType()); + verifyQualifier(qualifiers[0].annotationType(), customQualifiers); } else { Map, Integer> timesQualifierWasSeen = new HashMap<>(); for (Annotation qualifier : qualifiers) { - verifyQualifier(qualifier.annotationType()); + verifyQualifier(qualifier.annotationType(), customQualifiers); timesQualifierWasSeen.compute(qualifier.annotationType(), TimesSeenBiFunction.INSTANCE); } checkQualifiersForDuplicates(timesQualifierWasSeen); @@ -145,7 +145,11 @@ private static Object invoke(Method method, Object instance) { } } - private static void verifyQualifier(Class annotationType) { + private static void verifyQualifier(Class annotationType, Set customQualifiers) { + if (customQualifiers.contains(annotationType.getName())) { + return; + } + if (!annotationType.isAnnotationPresent(Qualifier.class)) { throw new IllegalArgumentException("Annotation is not a qualifier: " + annotationType); } diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/beanmanager/BeanManagerTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/beanmanager/BeanManagerTest.java index 98bab1fd35e40..9abf04e63b880 100644 --- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/beanmanager/BeanManagerTest.java +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/beanmanager/BeanManagerTest.java @@ -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; @@ -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; @@ -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> getAdditionalQualifiers() { + return Map.of(DotName.createSimple(Low.class.getName()), Set.of()); + } + }) + .build(); @Test public void testGetBeans() { @@ -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)); } @@ -200,6 +212,13 @@ public void testResolveObservers() { assertEquals(Number.class, observers.iterator().next().getObservedType()); } + @Test + public void testGetBeanWithCustomQualifier() { + BeanManager beanManager = Arc.container().beanManager(); + Set> beans = beanManager.getBeans(BeanWithCustomQualifier.class, Low.Literal.INSTANCE); + assertEquals(1, beans.size()); + } + @ApplicationScoped static class Observers { @@ -207,12 +226,25 @@ 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 implements Low { + public static final Literal INSTANCE = new Literal(); + } } @Dependent