Skip to content

Commit

Permalink
ArC - introduce immutable bean archive index
Browse files Browse the repository at this point in the history
- this is index is mandatory and is used to discover beans
- the computing index is optional and can be used for other tasks, e.g. during type-safe resolution
- if the computing index is not present the immutable index is used
instead
- fixes quarkusio#29575
  • Loading branch information
mkouba committed Dec 5, 2022
1 parent cdac72c commit f14342c
Show file tree
Hide file tree
Showing 13 changed files with 126 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,8 @@ public void transform(TransformationContext transformationContext) {
}
});

builder.setBeanArchiveIndex(index);
builder.setComputingBeanArchiveIndex(index);
builder.setImmutableBeanArchiveIndex(beanArchiveIndex.getImmutableIndex());
builder.setApplicationIndex(combinedIndex.getIndex());
List<BeanDefiningAnnotation> beanDefiningAnnotations = additionalBeanDefiningAnnotations.stream()
.map((s) -> new BeanDefiningAnnotation(s.getName(), s.getDefaultScope())).collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,42 @@
* Compared to {@link io.quarkus.deployment.builditem.CombinedIndexBuildItem} this index can contain additional classes
* that were indexed while bean discovery was in progress.
*
* It also holds information about all programmatically registered beans and all generated bean classes.
*
* @see GeneratedBeanBuildItem
* @see AdditionalBeanBuildItem
* @see io.quarkus.deployment.builditem.CombinedIndexBuildItem
*/
public final class BeanArchiveIndexBuildItem extends SimpleBuildItem {

private final IndexView index;
private final IndexView immutableIndex;
private final Set<DotName> generatedClassNames;

public BeanArchiveIndexBuildItem(IndexView index, Set<DotName> generatedClassNames) {
public BeanArchiveIndexBuildItem(IndexView index, IndexView immutableIndex, Set<DotName> generatedClassNames) {
this.index = index;
this.immutableIndex = immutableIndex;
this.generatedClassNames = generatedClassNames;
}

/**
* This index is built on top of the immutable index.
*
* @return the computing index that can also index classes on demand
*/
public IndexView getIndex() {
return index;
}

/**
*
* @return an immutable index that represents the bean archive
*/
public IndexView getImmutableIndex() {
return immutableIndex;
}

/**
*
* @return the set of classes generated via {@link GeneratedBeanBuildItem}
*/
public Set<DotName> getGeneratedClassNames() {
return generatedClassNames;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@ public BeanArchiveIndexBuildItem build(ArcConfig config, ApplicationArchivesBuil
additionalClasses.put(knownMissingClass, Optional.empty());
}

// Finally, index ArC/CDI API built-in classes
return new BeanArchiveIndexBuildItem(
BeanArchives.buildBeanArchiveIndex(Thread.currentThread().getContextClassLoader(), additionalClasses,
applicationIndex,
additionalBeanIndexer.complete()),
generatedClassNames);
IndexView immutableBeanArchiveIndex = BeanArchives.buildImmutableBeanArchiveIndex(applicationIndex,
additionalBeanIndexer.complete());
IndexView computingBeanArchiveIndex = BeanArchives.buildComputingBeanArchiveIndex(
Thread.currentThread().getContextClassLoader(),
additionalClasses, immutableBeanArchiveIndex);
return new BeanArchiveIndexBuildItem(computingBeanArchiveIndex, immutableBeanArchiveIndex, generatedClassNames);
}

private IndexView buildApplicationIndex(ArcConfig config, ApplicationArchivesBuildItem applicationArchivesBuildItem,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ public void getAnnotationsToAddBeanMethodWithScope() {
private IndexView getIndex(final Class<?>... classes) {
try {
Index index = Index.of(classes);
return BeanArchives.buildBeanArchiveIndex(getClass().getClassLoader(), new ConcurrentHashMap<>(), index);
return BeanArchives.buildComputingBeanArchiveIndex(getClass().getClassLoader(), new ConcurrentHashMap<>(),
BeanArchives.buildImmutableBeanArchiveIndex(index));
} catch (IOException e) {
throw new IllegalStateException("Failed to index classes", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class AnnotationLiteralProcessor {
generateAnnotationLiteralClassName(key.annotationName()),
applicationClassPredicate.test(key.annotationName()),
key.annotationClass));
this.beanArchiveIndex = beanArchiveIndex;
this.beanArchiveIndex = Objects.requireNonNull(beanArchiveIndex);
}

boolean hasLiteralsToGenerate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,23 @@ public final class BeanArchives {
/**
*
* @param applicationIndexes
* @return the final bean archive index
* @return the immutable bean archive index
*/
public static IndexView buildBeanArchiveIndex(ClassLoader deploymentClassLoader,
Map<DotName, Optional<ClassInfo>> additionalClasses, IndexView... applicationIndexes) {
public static IndexView buildImmutableBeanArchiveIndex(IndexView... applicationIndexes) {
List<IndexView> indexes = new ArrayList<>();
Collections.addAll(indexes, applicationIndexes);
indexes.add(buildAdditionalIndex());
return new IndexWrapper(CompositeIndex.create(indexes), deploymentClassLoader, additionalClasses);
return CompositeIndex.create(indexes);
}

/**
*
* @param wrappedIndexes
* @return the computing bean archive index
*/
public static IndexView buildComputingBeanArchiveIndex(ClassLoader deploymentClassLoader,
Map<DotName, Optional<ClassInfo>> additionalClasses, IndexView immutableIndex) {
return new IndexWrapper(immutableIndex, deploymentClassLoader, additionalClasses);
}

private static IndexView buildAdditionalIndex() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
Expand Down Expand Up @@ -55,8 +56,8 @@ public class BeanDeployment {

private final BuildContextImpl buildContext;

private final IndexView beanArchiveIndex;

private final IndexView beanArchiveComputingIndex;
private final IndexView beanArchiveImmutableIndex;
private final IndexView applicationIndex;

private final Map<DotName, ClassInfo> qualifiers;
Expand Down Expand Up @@ -124,7 +125,8 @@ public class BeanDeployment {
}
this.beanDefiningAnnotations = beanDefiningAnnotations;
this.resourceAnnotations = new HashSet<>(builder.resourceAnnotations);
this.beanArchiveIndex = builder.beanArchiveIndex;
this.beanArchiveComputingIndex = builder.beanArchiveComputingIndex;
this.beanArchiveImmutableIndex = Objects.requireNonNull(builder.beanArchiveImmutableIndex);
this.applicationIndex = builder.applicationIndex;
this.annotationStore = new AnnotationStore(initAndSort(builder.annotationTransformers, buildContext), buildContext);
if (buildContext != null) {
Expand All @@ -141,11 +143,11 @@ public class BeanDeployment {
this.excludeTypes = builder.excludeTypes != null ? new ArrayList<>(builder.excludeTypes) : Collections.emptyList();

qualifierNonbindingMembers = new HashMap<>();
qualifiers = findQualifiers(this.beanArchiveIndex);
qualifiers = findQualifiers();
for (QualifierRegistrar registrar : builder.qualifierRegistrars) {
for (Map.Entry<DotName, Set<String>> entry : registrar.getAdditionalQualifiers().entrySet()) {
DotName dotName = entry.getKey();
ClassInfo classInfo = getClassByName(this.beanArchiveIndex, dotName);
ClassInfo classInfo = getClassByName(getBeanArchiveIndex(), dotName);
if (classInfo != null) {
Set<String> nonbindingMembers = entry.getValue();
if (nonbindingMembers == null) {
Expand All @@ -156,15 +158,15 @@ public class BeanDeployment {
}
}
}
repeatingQualifierAnnotations = findContainerAnnotations(qualifiers, this.beanArchiveIndex);
repeatingQualifierAnnotations = findContainerAnnotations(qualifiers);
buildContextPut(Key.QUALIFIERS.asString(), Collections.unmodifiableMap(qualifiers));

interceptorNonbindingMembers = new HashMap<>();
interceptorBindings = findInterceptorBindings(this.beanArchiveIndex);
interceptorBindings = findInterceptorBindings();
for (InterceptorBindingRegistrar registrar : builder.interceptorBindingRegistrars) {
for (InterceptorBindingRegistrar.InterceptorBinding binding : registrar.getAdditionalBindings()) {
DotName dotName = binding.getName();
ClassInfo annotationClass = getClassByName(this.beanArchiveIndex, dotName);
ClassInfo annotationClass = getClassByName(getBeanArchiveIndex(), dotName);
if (annotationClass != null) {
Set<String> nonbinding = new HashSet<>();
for (MethodInfo method : annotationClass.methods()) {
Expand All @@ -177,20 +179,19 @@ public class BeanDeployment {
interceptorBindings.put(dotName, annotationClass);
}
}
repeatingInterceptorBindingAnnotations = findContainerAnnotations(interceptorBindings, this.beanArchiveIndex);
repeatingInterceptorBindingAnnotations = findContainerAnnotations(interceptorBindings);
buildContextPut(Key.INTERCEPTOR_BINDINGS.asString(), Collections.unmodifiableMap(interceptorBindings));

Set<DotName> additionalStereotypes = new HashSet<>();
for (StereotypeRegistrar stereotypeRegistrar : builder.stereotypeRegistrars) {
additionalStereotypes.addAll(stereotypeRegistrar.getAdditionalStereotypes());
}

this.stereotypes = findStereotypes(this.beanArchiveIndex, interceptorBindings, customContexts, additionalStereotypes,
this.stereotypes = findStereotypes(interceptorBindings, customContexts, additionalStereotypes,
annotationStore);
buildContextPut(Key.STEREOTYPES.asString(), Collections.unmodifiableMap(stereotypes));

this.transitiveInterceptorBindings = findTransitiveInterceptorBindings(interceptorBindings.keySet(),
this.beanArchiveIndex,
new HashMap<>(), interceptorBindings, annotationStore);

this.injectionPoints = new CopyOnWriteArrayList<>();
Expand All @@ -199,7 +200,7 @@ public class BeanDeployment {
this.beans = new CopyOnWriteArrayList<>();
this.observers = new CopyOnWriteArrayList<>();

this.assignabilityCheck = new AssignabilityCheck(beanArchiveIndex, applicationIndex);
this.assignabilityCheck = new AssignabilityCheck(getBeanArchiveIndex(), applicationIndex);
this.beanResolver = new BeanResolverImpl(this);
this.delegateInjectionPointResolver = new DelegateInjectionPointResolverImpl(this);
this.interceptorResolver = new InterceptorResolver(this);
Expand Down Expand Up @@ -517,12 +518,17 @@ public Collection<StereotypeInfo> getStereotypes() {
}

/**
* This index was used to discover components (beans, interceptors, qualifiers, etc.) and during type-safe resolution.
* Returns the index that was used during discovery and type-safe resolution.
* <p>
* In general, the returned index is usually "computing" which means that it attempts to compute the information for the
* classes that were not part of the initial bean archive index. I.e. the returned index corresponds to
* {@link BeanProcessor.Builder#setComputingBeanArchiveIndex(IndexView)}. However, if the computing index was not set then
* the index set by {@link BeanProcessor.Builder#setImmutableBeanArchiveIndex(IndexView)} is used instead.
*
* @return the bean archive index
*/
public IndexView getBeanArchiveIndex() {
return beanArchiveIndex;
return beanArchiveComputingIndex != null ? beanArchiveComputingIndex : beanArchiveImmutableIndex;
}

/**
Expand Down Expand Up @@ -670,9 +676,9 @@ private void buildContextPut(String key, Object value) {
}
}

private Map<DotName, ClassInfo> findQualifiers(IndexView index) {
private Map<DotName, ClassInfo> findQualifiers() {
Map<DotName, ClassInfo> qualifiers = new HashMap<>();
for (AnnotationInstance qualifier : index.getAnnotations(DotNames.QUALIFIER)) {
for (AnnotationInstance qualifier : beanArchiveImmutableIndex.getAnnotations(DotNames.QUALIFIER)) {
ClassInfo qualifierClass = qualifier.target().asClass();
if (isExcluded(qualifierClass)) {
continue;
Expand All @@ -682,23 +688,23 @@ private Map<DotName, ClassInfo> findQualifiers(IndexView index) {
return qualifiers;
}

private Map<DotName, ClassInfo> findContainerAnnotations(Map<DotName, ClassInfo> annotations, IndexView index) {
private Map<DotName, ClassInfo> findContainerAnnotations(Map<DotName, ClassInfo> annotations) {
Map<DotName, ClassInfo> containerAnnotations = new HashMap<>();
for (ClassInfo annotation : annotations.values()) {
AnnotationInstance repeatableMetaAnnotation = annotation.declaredAnnotation(DotNames.REPEATABLE);
if (repeatableMetaAnnotation != null) {
DotName containerAnnotationName = repeatableMetaAnnotation.value().asClass().name();
ClassInfo containerClass = getClassByName(index, containerAnnotationName);
ClassInfo containerClass = getClassByName(getBeanArchiveIndex(), containerAnnotationName);
containerAnnotations.put(containerAnnotationName, containerClass);
}
}
return containerAnnotations;
}

private Map<DotName, ClassInfo> findInterceptorBindings(IndexView index) {
private Map<DotName, ClassInfo> findInterceptorBindings() {
Map<DotName, ClassInfo> bindings = new HashMap<>();
// Note: doesn't use AnnotationStore, this will operate on classes without applying annotation transformers
for (AnnotationInstance binding : index.getAnnotations(DotNames.INTERCEPTOR_BINDING)) {
for (AnnotationInstance binding : beanArchiveImmutableIndex.getAnnotations(DotNames.INTERCEPTOR_BINDING)) {
ClassInfo bindingClass = binding.target().asClass();
if (isExcluded(bindingClass)) {
continue;
Expand All @@ -709,7 +715,6 @@ private Map<DotName, ClassInfo> findInterceptorBindings(IndexView index) {
}

private static Map<DotName, Set<AnnotationInstance>> findTransitiveInterceptorBindings(Collection<DotName> initialBindings,
IndexView index,
Map<DotName, Set<AnnotationInstance>> result, Map<DotName, ClassInfo> interceptorBindings,
AnnotationStore annotationStore) {
// for all known interceptor bindings
Expand Down Expand Up @@ -748,20 +753,20 @@ private static Set<AnnotationInstance> recursiveBuild(DotName name,
return result;
}

private Map<DotName, StereotypeInfo> findStereotypes(IndexView index, Map<DotName, ClassInfo> interceptorBindings,
private Map<DotName, StereotypeInfo> findStereotypes(Map<DotName, ClassInfo> interceptorBindings,
Map<ScopeInfo, Function<MethodCreator, ResultHandle>> customContexts,
Set<DotName> additionalStereotypes, AnnotationStore annotationStore) {

Map<DotName, StereotypeInfo> stereotypes = new HashMap<>();

Set<DotName> stereotypeNames = new HashSet<>();
for (AnnotationInstance annotation : index.getAnnotations(DotNames.STEREOTYPE)) {
for (AnnotationInstance annotation : beanArchiveImmutableIndex.getAnnotations(DotNames.STEREOTYPE)) {
stereotypeNames.add(annotation.target().asClass().name());
}
stereotypeNames.addAll(additionalStereotypes);

for (DotName stereotypeName : stereotypeNames) {
ClassInfo stereotypeClass = getClassByName(index, stereotypeName);
ClassInfo stereotypeClass = getClassByName(getBeanArchiveIndex(), stereotypeName);
if (stereotypeClass != null && !isExcluded(stereotypeClass)) {

boolean isAlternative = false;
Expand Down Expand Up @@ -852,7 +857,8 @@ private List<BeanInfo> findBeans(Collection<DotName> beanDefiningAnnotations, Li
.map(StereotypeInfo::getName)
.collect(Collectors.toSet());

for (ClassInfo beanClass : beanArchiveIndex.getKnownClasses()) {
// If needed use the specialized immutable index to discover beans
for (ClassInfo beanClass : beanArchiveImmutableIndex.getKnownClasses()) {

if (Modifier.isInterface(beanClass.flags()) || Modifier.isAbstract(beanClass.flags())
|| beanClass.isAnnotation() || beanClass.isEnum()) {
Expand Down Expand Up @@ -987,7 +993,7 @@ private List<BeanInfo> findBeans(Collection<DotName> beanDefiningAnnotations, Li
}
DotName superType = aClass.superName();
aClass = superType != null && !superType.equals(DotNames.OBJECT)
? getClassByName(beanArchiveIndex, superType)
? getClassByName(getBeanArchiveIndex(), superType)
: null;
}
for (FieldInfo field : beanClass.fields()) {
Expand Down Expand Up @@ -1233,7 +1239,7 @@ static void processErrors(List<Throwable> errors) {

private List<InterceptorInfo> findInterceptors(List<InjectionPointInfo> injectionPoints) {
Map<DotName, ClassInfo> interceptorClasses = new HashMap<>();
for (AnnotationInstance annotation : beanArchiveIndex.getAnnotations(DotNames.INTERCEPTOR)) {
for (AnnotationInstance annotation : beanArchiveImmutableIndex.getAnnotations(DotNames.INTERCEPTOR)) {
if (Kind.CLASS.equals(annotation.target().kind())) {
interceptorClasses.put(annotation.target().asClass().name(), annotation.target().asClass());
}
Expand All @@ -1260,7 +1266,7 @@ private List<InterceptorInfo> findInterceptors(List<InjectionPointInfo> injectio

private List<DecoratorInfo> findDecorators(List<InjectionPointInfo> injectionPoints) {
Map<DotName, ClassInfo> decoratorClasses = new HashMap<>();
for (AnnotationInstance annotation : beanArchiveIndex.getAnnotations(DotNames.DECORATOR)) {
for (AnnotationInstance annotation : beanArchiveImmutableIndex.getAnnotations(DotNames.DECORATOR)) {
if (Kind.CLASS.equals(annotation.target().kind())) {
decoratorClasses.put(annotation.target().asClass().name(), annotation.target().asClass());
}
Expand Down
Loading

0 comments on commit f14342c

Please sign in to comment.