From 8880a95ffbd43e47605c6f8caac4b9051fbdd267 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Tue, 8 Sep 2020 16:35:24 +0200 Subject: [PATCH] ArC - distinguish "bean archive" and "application" index - the first one is used to discover components and during type-safe resolution - the latter one is also used to discover types during type-safe resolution - fixes #11863 --- .../quarkus/arc/deployment/ArcProcessor.java | 4 +- .../arc/deployment/AutoAddScopeBuildItem.java | 1 - .../arc/processor/BeanConfigurator.java | 2 +- .../quarkus/arc/processor/BeanDeployment.java | 52 ++++++++++++----- .../quarkus/arc/processor/BeanGenerator.java | 8 +-- .../io/quarkus/arc/processor/BeanInfo.java | 9 +-- .../quarkus/arc/processor/BeanProcessor.java | 31 ++++++++-- .../quarkus/arc/processor/BeanResolver.java | 18 +++--- .../java/io/quarkus/arc/processor/Beans.java | 11 ++-- .../io/quarkus/arc/processor/BuiltinBean.java | 2 +- .../arc/processor/ClientProxyGenerator.java | 14 ++--- .../io/quarkus/arc/processor/Injection.java | 2 +- .../arc/processor/InterceptorGenerator.java | 2 +- .../arc/processor/InterceptorInfo.java | 6 +- .../arc/processor/InterceptorResolver.java | 5 +- .../io/quarkus/arc/processor/Methods.java | 4 +- .../arc/processor/SubclassGenerator.java | 2 +- .../java/io/quarkus/arc/processor/Types.java | 25 +++++--- .../arc/processor/BeanInfoInjectionsTest.java | 2 +- .../arc/processor/BeanInfoQualifiersTest.java | 3 +- .../arc/processor/BeanInfoTypesTest.java | 2 +- .../io/quarkus/arc/processor/TypesTest.java | 2 +- .../io/quarkus/arc/test/ArcTestContainer.java | 30 ++++++++-- .../arc/test/index/AdditionalIndexTest.java | 58 +++++++++++++++++++ 24 files changed, 216 insertions(+), 79 deletions(-) create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/index/AdditionalIndexTest.java diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java index cf31b7c46b214..04183da00e635 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java @@ -127,6 +127,7 @@ AdditionalBeanBuildItem quarkusApplication(CombinedIndexBuildItem combinedIndexB public ContextRegistrationPhaseBuildItem initialize( ArcConfig arcConfig, BeanArchiveIndexBuildItem beanArchiveIndex, + CombinedIndexBuildItem combinedIndex, ApplicationArchivesBuildItem applicationArchivesBuildItem, List annotationTransformers, List injectionPointTransformers, @@ -193,7 +194,8 @@ public void transform(TransformationContext transformationContext) { } } }); - builder.setIndex(index); + builder.setBeanArchiveIndex(index); + builder.setApplicationIndex(combinedIndex.getIndex()); List beanDefiningAnnotations = additionalBeanDefiningAnnotations.stream() .map((s) -> new BeanDefiningAnnotation(s.getName(), s.getDefaultScope())).collect(Collectors.toList()); beanDefiningAnnotations.add(new BeanDefiningAnnotation(ADDITIONAL_BEAN, null)); diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeBuildItem.java index 3991aade39ec1..d0c586447bc56 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeBuildItem.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/AutoAddScopeBuildItem.java @@ -169,7 +169,6 @@ public Builder containsAnnotations(DotName... annotationNames) { * The final predicate is a short-circuiting logical AND of the previous predicate (if any) and this condition. * * @param interfaceName - * @param index * @return self */ public Builder implementsInterface(DotName interfaceName) { diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfigurator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfigurator.java index 6306a2ac3097f..0d4e41ce96c1d 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfigurator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfigurator.java @@ -44,7 +44,7 @@ protected BeanConfigurator self() { */ public void done() { if (consumed.compareAndSet(false, true)) { - ClassInfo implClass = getClassByName(beanDeployment.getIndex(), Objects.requireNonNull(implClazz)); + ClassInfo implClass = getClassByName(beanDeployment.getBeanArchiveIndex(), Objects.requireNonNull(implClazz)); if (implClass == null) { throw new IllegalStateException("Unable to find the bean class in the index: " + implClazz); } 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 7fe848cc26bdb..58e864df5d517 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 @@ -56,7 +56,9 @@ public class BeanDeployment { private final BuildContextImpl buildContext; - private final IndexView index; + private final IndexView beanArchiveIndex; + + private final IndexView applicationIndex; private final Map qualifiers; @@ -108,7 +110,7 @@ public class BeanDeployment { private final List> excludeTypes; - BeanDeployment(IndexView index, BuildContextImpl buildContext, BeanProcessor.Builder builder) { + BeanDeployment(BuildContextImpl buildContext, BeanProcessor.Builder builder) { this.buildContext = buildContext; Set beanDefiningAnnotations = new HashSet<>(); if (builder.additionalBeanDefiningAnnotations != null) { @@ -116,7 +118,8 @@ public class BeanDeployment { } this.beanDefiningAnnotations = beanDefiningAnnotations; this.resourceAnnotations = new HashSet<>(builder.resourceAnnotations); - this.index = index; + this.beanArchiveIndex = builder.beanArchiveIndex; + this.applicationIndex = builder.applicationIndex; this.annotationStore = new AnnotationStore(initAndSort(builder.annotationTransformers, buildContext), buildContext); if (buildContext != null) { buildContext.putInternal(Key.ANNOTATION_STORE.asString(), annotationStore); @@ -130,16 +133,16 @@ public class BeanDeployment { this.customContexts = new ConcurrentHashMap<>(); - this.qualifiers = findQualifiers(index); - this.repeatingQualifierAnnotations = findContainerAnnotations(qualifiers, index); + this.qualifiers = findQualifiers(this.beanArchiveIndex); + this.repeatingQualifierAnnotations = findContainerAnnotations(qualifiers, this.beanArchiveIndex); buildContextPut(Key.QUALIFIERS.asString(), Collections.unmodifiableMap(qualifiers)); - this.interceptorBindings = findInterceptorBindings(index); + this.interceptorBindings = findInterceptorBindings(this.beanArchiveIndex); this.nonBindingFields = new HashMap<>(); for (InterceptorBindingRegistrar registrar : builder.additionalInterceptorBindingRegistrars) { for (Map.Entry> bindingEntry : registrar.registerAdditionalBindings().entrySet()) { DotName dotName = bindingEntry.getKey(); - ClassInfo classInfo = getClassByName(index, dotName); + ClassInfo classInfo = getClassByName(this.beanArchiveIndex, dotName); if (classInfo != null) { if (bindingEntry.getValue() != null) { nonBindingFields.put(dotName, bindingEntry.getValue()); @@ -150,11 +153,12 @@ public class BeanDeployment { } buildContextPut(Key.INTERCEPTOR_BINDINGS.asString(), Collections.unmodifiableMap(interceptorBindings)); - this.stereotypes = findStereotypes(index, interceptorBindings, beanDefiningAnnotations, customContexts, + this.stereotypes = findStereotypes(this.beanArchiveIndex, interceptorBindings, beanDefiningAnnotations, customContexts, builder.additionalStereotypes, annotationStore); buildContextPut(Key.STEREOTYPES.asString(), Collections.unmodifiableMap(stereotypes)); - this.transitiveInterceptorBindings = findTransitiveInterceptorBindigs(interceptorBindings.keySet(), index, + this.transitiveInterceptorBindings = findTransitiveInterceptorBindigs(interceptorBindings.keySet(), + this.beanArchiveIndex, new HashMap<>(), interceptorBindings, annotationStore); this.injectionPoints = new CopyOnWriteArrayList<>(); @@ -356,8 +360,28 @@ public Collection getInterceptors() { return interceptors; } - public IndexView getIndex() { - return index; + /** + * This index was used to discover components (beans, interceptors, qualifiers, etc.) and during type-safe resolution. + * + * @return the bean archive index + */ + public IndexView getBeanArchiveIndex() { + return beanArchiveIndex; + } + + /** + * This index is optional and is used to discover types during type-safe resolution. + *

+ * Some types may not be part of the bean archive index but are still needed during type-safe resolution. + * + * @return the application index or {@code null} + */ + public IndexView getApplicationIndex() { + return applicationIndex; + } + + boolean hasApplicationIndex() { + return applicationIndex != null; } BeanResolver getBeanResolver() { @@ -631,7 +655,7 @@ private List findBeans(Collection beanDefiningAnnotations, Li .map(Entry::getKey) .collect(Collectors.toList()); - for (ClassInfo beanClass : index.getKnownClasses()) { + for (ClassInfo beanClass : beanArchiveIndex.getKnownClasses()) { if (Modifier.isInterface(beanClass.flags()) || Modifier.isAbstract(beanClass.flags()) // Replace with ClassInfo#isAnnotation() and ClassInfo#isEnum() when using Jandex 2.1.4+ @@ -769,7 +793,7 @@ private List findBeans(Collection beanDefiningAnnotations, Li Type superType = aClass.superClassType(); aClass = superType != null && !superType.name().equals(DotNames.OBJECT) && CLASS_TYPES.contains(superType.kind()) - ? getClassByName(index, superType.name()) + ? getClassByName(beanArchiveIndex, superType.name()) : null; } for (FieldInfo field : beanClass.fields()) { @@ -1013,7 +1037,7 @@ static void processErrors(List errors) { private List findInterceptors(List injectionPoints) { Set interceptorClasses = new HashSet<>(); - for (AnnotationInstance annotation : index.getAnnotations(DotNames.INTERCEPTOR)) { + for (AnnotationInstance annotation : beanArchiveIndex.getAnnotations(DotNames.INTERCEPTOR)) { if (Kind.CLASS.equals(annotation.target().kind())) { interceptorClasses.add(annotation.target().asClass()); } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java index d66816efe2d49..9f23bbf85a5c6 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java @@ -742,7 +742,7 @@ protected void implementDestroy(BeanInfo bean, ClassCreator beanCreator, String // PreDestroy callbacks List preDestroyCallbacks = Beans.getCallbacks(bean.getTarget().get().asClass(), DotNames.PRE_DESTROY, - bean.getDeployment().getIndex()); + bean.getDeployment().getBeanArchiveIndex()); for (MethodInfo callback : preDestroyCallbacks) { if (Modifier.isPrivate(callback.flags())) { privateMembers.add(isApplicationClass, String.format("@PreDestroy callback %s#%s()", @@ -1483,7 +1483,7 @@ void implementCreateForClassBean(ClassOutput classOutput, ClassCreator beanCreat if (!bean.isInterceptor()) { List postConstructCallbacks = Beans.getCallbacks(bean.getTarget().get().asClass(), DotNames.POST_CONSTRUCT, - bean.getDeployment().getIndex()); + bean.getDeployment().getBeanArchiveIndex()); for (MethodInfo callback : postConstructCallbacks) { if (isReflectionFallbackNeeded(callback, targetPackage)) { if (Modifier.isPrivate(callback.flags())) { @@ -1531,7 +1531,7 @@ protected void implementGet(BeanInfo bean, ClassCreator beanCreator, String prov canBeOptimized = bean.getLifecycleInterceptors(InterceptionType.PRE_DESTROY).isEmpty() && Beans.getCallbacks(bean.getTarget().get().asClass(), DotNames.PRE_DESTROY, - bean.getDeployment().getIndex()).isEmpty(); + bean.getDeployment().getBeanArchiveIndex()).isEmpty(); } else if (bean.isProducerMethod() || bean.isProducerField()) { canBeOptimized = bean.getDisposer() == null; } @@ -1817,7 +1817,7 @@ static ResultHandle collectInjectionPointAnnotations(ClassOutput classOutput, Cl .readStaticField(FieldDescriptor.of(InjectLiteral.class, "INSTANCE", InjectLiteral.class)); } else { // Create annotation literal if needed - ClassInfo literalClass = getClassByName(beanDeployment.getIndex(), annotation.name()); + ClassInfo literalClass = getClassByName(beanDeployment.getBeanArchiveIndex(), annotation.name()); annotationHandle = annotationLiterals.process(constructor, classOutput, literalClass, annotation, Types.getPackageName(beanCreator.getClassName())); diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java index 81b741416c1d1..ef3ad0a93c1d4 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java @@ -281,7 +281,8 @@ boolean hasDefaultDestroy() { } if (isClassBean()) { return getLifecycleInterceptors(InterceptionType.PRE_DESTROY).isEmpty() - && Beans.getCallbacks(target.get().asClass(), DotNames.PRE_DESTROY, beanDeployment.getIndex()).isEmpty(); + && Beans.getCallbacks(target.get().asClass(), DotNames.PRE_DESTROY, beanDeployment.getBeanArchiveIndex()) + .isEmpty(); } else { return disposer == null && destroyerConsumer == null; } @@ -452,7 +453,7 @@ private void addClassLevelBindings(ClassInfo classInfo, Collection e.name().equals(a.name()))) .forEach(a -> bindings.add(a)); if (classInfo.superClassType() != null && !classInfo.superClassType().name().equals(DotNames.OBJECT)) { - ClassInfo superClass = getClassByName(beanDeployment.getIndex(), classInfo.superName()); + ClassInfo superClass = getClassByName(beanDeployment.getBeanArchiveIndex(), classInfo.superName()); if (superClass != null) { addClassLevelBindings(superClass, bindings); } @@ -536,9 +537,9 @@ private ClassInfo initImplClazz(AnnotationTarget target, BeanDeployment beanDepl case CLASS: return target.asClass(); case FIELD: - return getClassByName(beanDeployment.getIndex(), target.asField().type()); + return getClassByName(beanDeployment.getBeanArchiveIndex(), target.asField().type()); case METHOD: - return getClassByName(beanDeployment.getIndex(), target.asMethod().returnType()); + return getClassByName(beanDeployment.getBeanArchiveIndex(), target.asMethod().returnType()); default: break; } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanProcessor.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanProcessor.java index 2d56682c86f79..951a3c97f4c6d 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanProcessor.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanProcessor.java @@ -84,13 +84,13 @@ private BeanProcessor(Builder builder) { // Initialize all build processors buildContext = new BuildContextImpl(); - buildContext.putInternal(Key.INDEX.asString(), builder.index); + buildContext.putInternal(Key.INDEX.asString(), builder.beanArchiveIndex); this.beanRegistrars = initAndSort(builder.beanRegistrars, buildContext); this.observerRegistrars = initAndSort(builder.observerRegistrars, buildContext); this.contextRegistrars = initAndSort(builder.contextRegistrars, buildContext); this.beanDeploymentValidators = initAndSort(builder.beanDeploymentValidators, buildContext); - this.beanDeployment = new BeanDeployment(builder.index, buildContext, builder); + this.beanDeployment = new BeanDeployment(buildContext, builder); // Make it configurable if we find that the set of annotations needs to grow this.injectionPointAnnotationsPredicate = annotationName -> !annotationName.equals(DotNames.DEPRECATED); @@ -247,7 +247,8 @@ public static class Builder { String name = DEFAULT_NAME; - IndexView index; + IndexView beanArchiveIndex; + IndexView applicationIndex; Collection additionalBeanDefiningAnnotations = Collections.emptySet(); Map> additionalStereotypes = Collections.emptyMap(); @@ -290,8 +291,28 @@ public Builder setName(String name) { return this; } - public Builder setIndex(IndexView index) { - this.index = index; + /** + * Set the bean archive index. This index is mandatory and is used to discover components (beans, interceptors, + * qualifiers, etc.) and during type-safe resolution. + * + * @param beanArchiveIndex + * @return self + */ + public Builder setBeanArchiveIndex(IndexView beanArchiveIndex) { + this.beanArchiveIndex = beanArchiveIndex; + return this; + } + + /** + * Set the application index. This index is optional and is also used to discover types during type-safe resolution. + *

+ * Some types may not be part of the bean archive index but are still needed during type-safe resolution. + * + * @param applicationIndex + * @return self + */ + public Builder setApplicationIndex(IndexView applicationIndex) { + this.applicationIndex = applicationIndex; return this; } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolver.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolver.java index debc15a8f725e..247b9a194d5bb 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolver.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolver.java @@ -9,7 +9,6 @@ import io.quarkus.arc.processor.InjectionPointInfo.TypeAndQualifiers; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -27,8 +26,7 @@ import org.jboss.jandex.WildcardType; /** - * - * @author Martin Kouba + * Implements type-safe resolution rules. */ class BeanResolver { @@ -45,14 +43,20 @@ public BeanResolver(BeanDeployment beanDeployment) { this.assignableFromMap = new ConcurrentHashMap<>(); this.assignableFromMapFunction = name -> { Set assignables = new HashSet<>(); - Collection subclasses = beanDeployment.getIndex().getAllKnownSubclasses(name); - for (ClassInfo subclass : subclasses) { + for (ClassInfo subclass : beanDeployment.getBeanArchiveIndex().getAllKnownSubclasses(name)) { assignables.add(subclass.name()); } - Collection implementors = beanDeployment.getIndex().getAllKnownImplementors(name); - for (ClassInfo implementor : implementors) { + for (ClassInfo implementor : beanDeployment.getBeanArchiveIndex().getAllKnownImplementors(name)) { assignables.add(implementor.name()); } + if (beanDeployment.hasApplicationIndex()) { + for (ClassInfo subclass : beanDeployment.getApplicationIndex().getAllKnownSubclasses(name)) { + assignables.add(subclass.name()); + } + for (ClassInfo implementor : beanDeployment.getApplicationIndex().getAllKnownImplementors(name)) { + assignables.add(implementor.name()); + } + } return assignables; }; this.resolved = new ConcurrentHashMap<>(); diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java index 7f7cd2521fa58..8a4452558b3fb 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java @@ -151,7 +151,7 @@ static BeanInfo createClassBean(ClassInfo beanClass, BeanDeployment beanDeployme private static ScopeInfo inheritScope(ClassInfo beanClass, BeanDeployment beanDeployment) { DotName superClassName = beanClass.superName(); while (!superClassName.equals(DotNames.OBJECT)) { - ClassInfo classFromIndex = getClassByName(beanDeployment.getIndex(), superClassName); + ClassInfo classFromIndex = getClassByName(beanDeployment.getBeanArchiveIndex(), superClassName); if (classFromIndex == null) { // class not in index LOGGER.warnf("Unable to determine scope for bean %s using inheritance because its super class " + @@ -680,7 +680,7 @@ static void validateBean(BeanInfo bean, List errors, List errors, List errors, List generate(BeanInfo bean, String beanClassName, generateSources); Type providerType = bean.getProviderType(); - ClassInfo providerClass = getClassByName(bean.getDeployment().getIndex(), providerType.name()); + ClassInfo providerClass = getClassByName(bean.getDeployment().getBeanArchiveIndex(), providerType.name()); String providerTypeName = providerClass.name().toString(); String baseName = getBaseName(bean, beanClassName); String targetPackage = getPackageName(bean); @@ -316,7 +316,7 @@ Collection getDelegatingMethods(BeanInfo bean, Consumer methodsFromWhichToRemoveFinal = new HashSet<>(); ClassInfo classInfo = bean.getTarget().get().asClass(); - Methods.addDelegatingMethods(bean.getDeployment().getIndex(), classInfo, + Methods.addDelegatingMethods(bean.getDeployment().getBeanArchiveIndex(), classInfo, methods, methodsFromWhichToRemoveFinal, transformUnproxyableClasses); if (!methodsFromWhichToRemoveFinal.isEmpty()) { String className = classInfo.name().toString(); @@ -325,16 +325,16 @@ Collection getDelegatingMethods(BeanInfo bean, Consumer generate(InterceptorInfo interceptor) { } else { baseName = DotNames.simpleName(interceptorClass); } - ClassInfo providerClass = getClassByName(interceptor.getDeployment().getIndex(), providerType.name()); + ClassInfo providerClass = getClassByName(interceptor.getDeployment().getBeanArchiveIndex(), providerType.name()); String providerTypeName = providerClass.name().toString(); String targetPackage = DotNames.packageName(providerType.name()); String generatedName = generatedNameFromTarget(targetPackage, baseName, BEAN_SUFFIX); diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptorInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptorInfo.java index acb66bdef6973..726b79e5ccc63 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptorInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptorInfo.java @@ -58,11 +58,7 @@ public class InterceptorInfo extends BeanInfo implements Comparable preDestroys = new ArrayList<>(); ClassInfo aClass = target.asClass(); - Set scanned = new HashSet<>(); while (aClass != null) { - if (!scanned.add(aClass.name())) { - continue; - } for (MethodInfo method : aClass.methods()) { if (Modifier.isStatic(method.flags())) { continue; @@ -95,7 +91,7 @@ public class InterceptorInfo extends BeanInfo implements Comparable nonBindingFields = beanDeployment.getNonBindingFields(interceptorBinding.name()); - for (AnnotationValue value : candidate.valuesWithDefaults(beanDeployment.getIndex())) { + for (AnnotationValue value : candidate.valuesWithDefaults(beanDeployment.getBeanArchiveIndex())) { String annotationField = value.name(); if (!interceptorBindingClass.method(annotationField).hasAnnotation(DotNames.NONBINDING) && !nonBindingFields.contains(annotationField) - && !value.equals(interceptorBinding.valueWithDefault(beanDeployment.getIndex(), annotationField))) { + && !value.equals( + interceptorBinding.valueWithDefault(beanDeployment.getBeanArchiveIndex(), annotationField))) { matches = false; break; } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java index fc66a35da0ee4..7d0dc3954907f 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java @@ -188,7 +188,7 @@ static Set addInterceptedMethodCandidates(BeanDeployment beanDeploym } if (classInfo.superClassType() != null) { - ClassInfo superClassInfo = getClassByName(beanDeployment.getIndex(), classInfo.superName()); + ClassInfo superClassInfo = getClassByName(beanDeployment.getBeanArchiveIndex(), classInfo.superName()); if (superClassInfo != null) { finalMethodsFoundAndNotChanged.addAll(addInterceptedMethodCandidates(beanDeployment, superClassInfo, candidates, classLevelBindings, bytecodeTransformerConsumer, transformUnproxyableClasses)); @@ -197,7 +197,7 @@ static Set addInterceptedMethodCandidates(BeanDeployment beanDeploym // Interface default methods can be intercepted too for (DotName i : classInfo.interfaceNames()) { - ClassInfo interfaceInfo = getClassByName(beanDeployment.getIndex(), i); + ClassInfo interfaceInfo = getClassByName(beanDeployment.getBeanArchiveIndex(), i); if (interfaceInfo != null) { //interfaces can't have final methods addInterceptedMethodCandidates(beanDeployment, interfaceInfo, candidates, diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java index 5879344c35883..324d19a971f5d 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java @@ -108,7 +108,7 @@ Collection generate(BeanInfo bean, String beanClassName) { generateSources); Type providerType = bean.getProviderType(); - ClassInfo providerClass = getClassByName(bean.getDeployment().getIndex(), providerType.name()); + ClassInfo providerClass = getClassByName(bean.getDeployment().getBeanArchiveIndex(), providerType.name()); String providerTypeName = providerClass.name().toString(); String baseName = getBaseName(bean, beanClassName); String generatedName = generatedName(providerType.name(), baseName); diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java index d092300cf035f..fc01ab5fe3ecf 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java @@ -19,6 +19,7 @@ import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; +import javax.enterprise.inject.spi.DefinitionException; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.AnnotationValue; @@ -164,13 +165,16 @@ static Type getProviderType(ClassInfo classInfo) { static Set getProducerMethodTypeClosure(MethodInfo producerMethod, BeanDeployment beanDeployment) { Set types; Type returnType = producerMethod.returnType(); + if (returnType.kind() == Kind.TYPE_VARIABLE) { + throw new DefinitionException("A type variable is not a legal bean type: " + producerMethod); + } if (returnType.kind() == Kind.PRIMITIVE || returnType.kind() == Kind.ARRAY) { types = new HashSet<>(); types.add(returnType); types.add(OBJECT_TYPE); return types; } else { - ClassInfo returnTypeClassInfo = getClassByName(beanDeployment.getIndex(), returnType); + ClassInfo returnTypeClassInfo = getClassByName(beanDeployment.getBeanArchiveIndex(), returnType); if (returnTypeClassInfo == null) { throw new IllegalArgumentException( "Producer method return type not found in index: " + producerMethod.returnType().name()); @@ -180,7 +184,7 @@ static Set getProducerMethodTypeClosure(MethodInfo producerMethod, BeanDep } else if (Kind.PARAMETERIZED_TYPE.equals(returnType.kind())) { types = getTypeClosure(returnTypeClassInfo, producerMethod, buildResolvedMap(returnType.asParameterizedType().arguments(), returnTypeClassInfo.typeParameters(), - Collections.emptyMap(), beanDeployment.getIndex()), + Collections.emptyMap(), beanDeployment.getBeanArchiveIndex()), beanDeployment, null); } else { throw new IllegalArgumentException("Unsupported return type"); @@ -192,12 +196,15 @@ static Set getProducerMethodTypeClosure(MethodInfo producerMethod, BeanDep static Set getProducerFieldTypeClosure(FieldInfo producerField, BeanDeployment beanDeployment) { Set types; Type fieldType = producerField.type(); + if (fieldType.kind() == Kind.TYPE_VARIABLE) { + throw new DefinitionException("A type variable is not a legal bean type: " + producerField); + } if (fieldType.kind() == Kind.PRIMITIVE || fieldType.kind() == Kind.ARRAY) { types = new HashSet<>(); types.add(fieldType); types.add(OBJECT_TYPE); } else { - ClassInfo fieldClassInfo = getClassByName(beanDeployment.getIndex(), producerField.type()); + ClassInfo fieldClassInfo = getClassByName(beanDeployment.getBeanArchiveIndex(), producerField.type()); if (fieldClassInfo == null) { throw new IllegalArgumentException("Producer field type not found in index: " + producerField.type().name()); } @@ -206,7 +213,7 @@ static Set getProducerFieldTypeClosure(FieldInfo producerField, BeanDeploy } else if (Kind.PARAMETERIZED_TYPE.equals(fieldType.kind())) { types = getTypeClosure(fieldClassInfo, producerField, buildResolvedMap(fieldType.asParameterizedType().arguments(), fieldClassInfo.typeParameters(), - Collections.emptyMap(), beanDeployment.getIndex()), + Collections.emptyMap(), beanDeployment.getBeanArchiveIndex()), beanDeployment, null); } else { throw new IllegalArgumentException("Unsupported return type"); @@ -222,7 +229,7 @@ static Set getClassBeanTypeClosure(ClassInfo classInfo, BeanDeployment bea types = getTypeClosure(classInfo, null, Collections.emptyMap(), beanDeployment, null); } else { types = getTypeClosure(classInfo, null, buildResolvedMap(typeParameters, typeParameters, - Collections.emptyMap(), beanDeployment.getIndex()), beanDeployment, null); + Collections.emptyMap(), beanDeployment.getBeanArchiveIndex()), beanDeployment, null); } return restrictBeanTypes(types, beanDeployment.getAnnotations(classInfo)); } @@ -269,12 +276,12 @@ static Set getTypeClosure(ClassInfo classInfo, AnnotationTarget producerFi if (BANNED_INTERFACE_TYPES.contains(interfaceType.name())) { continue; } - ClassInfo interfaceClassInfo = getClassByName(beanDeployment.getIndex(), interfaceType.name()); + ClassInfo interfaceClassInfo = getClassByName(beanDeployment.getBeanArchiveIndex(), interfaceType.name()); if (interfaceClassInfo != null) { Map resolved = Collections.emptyMap(); if (Kind.PARAMETERIZED_TYPE.equals(interfaceType.kind())) { resolved = buildResolvedMap(interfaceType.asParameterizedType().arguments(), - interfaceClassInfo.typeParameters(), resolvedTypeParameters, beanDeployment.getIndex()); + interfaceClassInfo.typeParameters(), resolvedTypeParameters, beanDeployment.getBeanArchiveIndex()); } types.addAll(getTypeClosure(interfaceClassInfo, producerFieldOrMethod, resolved, beanDeployment, resolvedTypeVariablesConsumer)); @@ -282,13 +289,13 @@ static Set getTypeClosure(ClassInfo classInfo, AnnotationTarget producerFi } // Superclass if (classInfo.superClassType() != null) { - ClassInfo superClassInfo = getClassByName(beanDeployment.getIndex(), classInfo.superName()); + ClassInfo superClassInfo = getClassByName(beanDeployment.getBeanArchiveIndex(), classInfo.superName()); if (superClassInfo != null) { Map resolved = Collections.emptyMap(); if (Kind.PARAMETERIZED_TYPE.equals(classInfo.superClassType().kind())) { resolved = buildResolvedMap(classInfo.superClassType().asParameterizedType().arguments(), superClassInfo.typeParameters(), - resolvedTypeParameters, beanDeployment.getIndex()); + resolvedTypeParameters, beanDeployment.getBeanArchiveIndex()); } types.addAll(getTypeClosure(superClassInfo, producerFieldOrMethod, resolved, beanDeployment, resolvedTypeVariablesConsumer)); diff --git a/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/BeanInfoInjectionsTest.java b/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/BeanInfoInjectionsTest.java index 12bddc9e90371..7cc59d4899d83 100644 --- a/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/BeanInfoInjectionsTest.java +++ b/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/BeanInfoInjectionsTest.java @@ -40,7 +40,7 @@ public void testInjections() throws IOException { Type listStringType = ParameterizedType.create(name(List.class), new Type[] { Type.create(name(String.class), Kind.CLASS) }, null); - BeanDeployment deployment = BeanProcessor.builder().setIndex(index).build().getBeanDeployment(); + BeanDeployment deployment = BeanProcessor.builder().setBeanArchiveIndex(index).build().getBeanDeployment(); deployment.registerCustomContexts(Collections.emptyList()); deployment.registerBeans(Collections.emptyList()); BeanInfo barBean = deployment.getBeans().stream().filter(b -> b.getTarget().get().equals(barClass)).findFirst().get(); diff --git a/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/BeanInfoQualifiersTest.java b/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/BeanInfoQualifiersTest.java index c000f4b729fe9..9243557ae4fa8 100644 --- a/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/BeanInfoQualifiersTest.java +++ b/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/BeanInfoQualifiersTest.java @@ -34,7 +34,8 @@ public void testQualifiers() throws IOException { DotName fooQualifierName = name(FooQualifier.class); ClassInfo fooClass = index.getClassByName(fooName); - BeanInfo bean = Beans.createClassBean(fooClass, BeanProcessor.builder().setIndex(index).build().getBeanDeployment(), + BeanInfo bean = Beans.createClassBean(fooClass, + BeanProcessor.builder().setBeanArchiveIndex(index).build().getBeanDeployment(), null); AnnotationInstance requiredFooQualifier = index.getAnnotations(fooQualifierName).stream() diff --git a/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/BeanInfoTypesTest.java b/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/BeanInfoTypesTest.java index c5278cbec82a0..f37e30b9f7a46 100644 --- a/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/BeanInfoTypesTest.java +++ b/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/BeanInfoTypesTest.java @@ -35,7 +35,7 @@ public void testResolver() throws IOException { Collection.class, List.class, Iterable.class, Object.class, String.class); - BeanDeployment deployment = BeanProcessor.builder().setIndex(index).build().getBeanDeployment(); + BeanDeployment deployment = BeanProcessor.builder().setBeanArchiveIndex(index).build().getBeanDeployment(); DotName fooName = name(Foo.class); ClassInfo fooClass = index.getClassByName(fooName); diff --git a/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/TypesTest.java b/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/TypesTest.java index 320d82da4fbd2..4a3fbd59ea94c 100644 --- a/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/TypesTest.java +++ b/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/TypesTest.java @@ -32,7 +32,7 @@ public void testGetTypeClosure() throws IOException { DotName producerName = DotName.createSimple(Producer.class.getName()); ClassInfo fooClass = index.getClassByName(fooName); Map> resolvedTypeVariables = new HashMap<>(); - BeanDeployment dummyDeployment = BeanProcessor.builder().setIndex(index).build().getBeanDeployment(); + BeanDeployment dummyDeployment = BeanProcessor.builder().setBeanArchiveIndex(index).build().getBeanDeployment(); // Baz, Foo, Object Set bazTypes = Types.getTypeClosure(index.getClassByName(bazName), null, diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/ArcTestContainer.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/ArcTestContainer.java index 9e68b64c2a732..73af347372eee 100644 --- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/ArcTestContainer.java +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/ArcTestContainer.java @@ -65,6 +65,7 @@ public static class Builder { private final List> resourceReferenceProviders; private final List> beanClasses; + private final List> additionalClasses; private final List> resourceAnnotations; private final List beanRegistrars; private final List observerRegistrars; @@ -82,6 +83,7 @@ public static class Builder { public Builder() { resourceReferenceProviders = new ArrayList<>(); beanClasses = new ArrayList<>(); + additionalClasses = new ArrayList<>(); resourceAnnotations = new ArrayList<>(); beanRegistrars = new ArrayList<>(); observerRegistrars = new ArrayList<>(); @@ -104,6 +106,11 @@ public Builder beanClasses(Class... beanClasses) { return this; } + public Builder additionalClasses(Class... additionalClasses) { + Collections.addAll(this.additionalClasses, additionalClasses); + return this; + } + @SafeVarargs public final Builder resourceAnnotations(Class... resourceAnnotations) { Collections.addAll(this.resourceAnnotations, resourceAnnotations); @@ -179,6 +186,7 @@ public ArcTestContainer build() { private final List> resourceReferenceProviders; private final List> beanClasses; + private final List> additionalClasses; private final List> resourceAnnotations; @@ -209,6 +217,7 @@ public ArcTestContainer build() { public ArcTestContainer(Class... beanClasses) { this.resourceReferenceProviders = Collections.emptyList(); this.beanClasses = Arrays.asList(beanClasses); + this.additionalClasses = Collections.emptyList(); this.resourceAnnotations = Collections.emptyList(); this.beanRegistrars = Collections.emptyList(); this.observerRegistrars = Collections.emptyList(); @@ -228,6 +237,7 @@ public ArcTestContainer(Class... beanClasses) { public ArcTestContainer(Builder builder) { this.resourceReferenceProviders = builder.resourceReferenceProviders; this.beanClasses = builder.beanClasses; + this.additionalClasses = builder.additionalClasses; this.resourceAnnotations = builder.resourceAnnotations; this.beanRegistrars = builder.beanRegistrars; this.observerRegistrars = builder.observerRegistrars; @@ -294,13 +304,24 @@ private ClassLoader init(ExtensionContext context) { Arc.shutdown(); // Build index - Index index; + Index beanArchiveIndex; try { - index = index(beanClasses); + beanArchiveIndex = index(beanClasses); } catch (IOException e) { throw new IllegalStateException("Failed to create index", e); } + Index applicationIndex; + if (additionalClasses.isEmpty()) { + applicationIndex = null; + } else { + try { + applicationIndex = index(additionalClasses); + } catch (IOException e) { + throw new IllegalStateException("Failed to create index", e); + } + } + ClassLoader old = Thread.currentThread() .getContextClassLoader(); @@ -331,8 +352,9 @@ private ClassLoader init(ExtensionContext context) { BeanProcessor.Builder builder = BeanProcessor.builder() .setName(testClass.getSimpleName()) - .setIndex(BeanArchives.buildBeanArchiveIndex(getClass().getClassLoader(), - new BeanArchives.PersistentClassIndex(), index)); + .setBeanArchiveIndex(BeanArchives.buildBeanArchiveIndex(getClass().getClassLoader(), + new BeanArchives.PersistentClassIndex(), beanArchiveIndex)) + .setApplicationIndex(applicationIndex); if (!resourceAnnotations.isEmpty()) { builder.addResourceAnnotations(resourceAnnotations.stream() .map(c -> DotName.createSimple(c.getName())) diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/index/AdditionalIndexTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/index/AdditionalIndexTest.java new file mode 100644 index 0000000000000..ca43a536541e6 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/index/AdditionalIndexTest.java @@ -0,0 +1,58 @@ +package io.quarkus.arc.test.index; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.test.ArcTestContainer; +import java.util.ArrayList; +import java.util.List; +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Produces; +import javax.inject.Inject; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class AdditionalIndexTest { + + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder() + .beanClasses(Client.class, SuperProducer.class, SuperClazz.class) + // SubClazz is not part of the bean archive index + .additionalClasses(SubClazz.class).build(); + + @Test + public void testTypesafeResolution() { + Client client = Arc.container().instance(Client.class).get(); + // Test that the build does not fail and we're able to inject the producer + assertTrue(client.list.isEmpty()); + } + + @Dependent + static class Client { + + // This injection point should be satisfied by SuperProducer#produce() + // because SubClazz extends SuperClazz + @Inject + List list; + } + + public static class SuperProducer { + + @Produces + List produce() { + return new ArrayList<>(); + } + + } + + public static class SuperClazz { + + public void ping() { + } + } + + public static class SubClazz extends SuperClazz { + + } + +}