Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ArC fixes for spec compatibility, round 4 #31248

Merged
merged 4 commits into from
Feb 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
*
* The annotated method can access other injected beans.
*/
public class ShutdownEvent {
public class ShutdownEvent extends jakarta.enterprise.event.Shutdown {

private final ShutdownReason shutdownReason;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
* The annotated method can access other injected beans.
*
*/
public class StartupEvent {
public class StartupEvent extends jakarta.enterprise.event.Startup {
}
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,9 @@ void initTestApplicationClassPredicateBean(ArcRecorder recorder, BeanContainerBu
@BuildStep
List<AdditionalApplicationArchiveMarkerBuildItem> marker() {
return Arrays.asList(new AdditionalApplicationArchiveMarkerBuildItem("META-INF/beans.xml"),
new AdditionalApplicationArchiveMarkerBuildItem("META-INF/services/jakarta.enterprise.inject.spi.Extension"));
new AdditionalApplicationArchiveMarkerBuildItem("META-INF/services/jakarta.enterprise.inject.spi.Extension"),
new AdditionalApplicationArchiveMarkerBuildItem(
"META-INF/services/jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension"));
}

@BuildStep
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.quarkus.arc.deployment;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
Expand All @@ -17,6 +20,7 @@
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.Indexer;
import org.jboss.logging.Logger;

import io.quarkus.arc.processor.BeanArchives;
import io.quarkus.arc.processor.BeanDefiningAnnotation;
Expand All @@ -37,6 +41,8 @@

public class BeanArchiveProcessor {

private static final Logger LOGGER = Logger.getLogger(BeanArchiveProcessor.class);

@BuildStep
public BeanArchiveIndexBuildItem build(ArcConfig config, ApplicationArchivesBuildItem applicationArchivesBuildItem,
List<BeanDefiningAnnotationBuildItem> additionalBeanDefiningAnnotations,
Expand Down Expand Up @@ -127,8 +133,12 @@ private IndexView buildApplicationIndex(ArcConfig config, ApplicationArchivesBui
if (isApplicationArchiveExcluded(config, excludeDependencyBuildItems, archive)) {
continue;
}
if (!possiblyBeanArchive(archive)) {
continue;
}
IndexView index = archive.getIndex();
if (isExplicitBeanArchive(archive) || isImplicitBeanArchive(index, beanDefiningAnnotations)
if (isExplicitBeanArchive(archive)
|| isImplicitBeanArchive(index, beanDefiningAnnotations)
|| isAdditionalBeanArchive(archive, beanArchivePredicates)) {
indexes.add(index);
}
Expand All @@ -144,6 +154,7 @@ private boolean isExplicitBeanArchive(ApplicationArchive archive) {
private boolean isImplicitBeanArchive(IndexView index, Set<DotName> beanDefiningAnnotations) {
// NOTE: Implicit bean archive without beans.xml contains one or more bean classes with a bean defining annotation and no extension
return index.getAllKnownImplementors(DotNames.EXTENSION).isEmpty()
&& index.getAllKnownImplementors(DotNames.BUILD_COMPATIBLE_EXTENSION).isEmpty()
&& containsBeanDefiningAnnotation(index, beanDefiningAnnotations);
}

Expand All @@ -157,6 +168,40 @@ private boolean isAdditionalBeanArchive(ApplicationArchive archive,
return false;
}

private boolean possiblyBeanArchive(ApplicationArchive archive) {
return archive.apply(tree -> {
boolean result = true;
for (String beansXml : List.of("META-INF/beans.xml", "WEB-INF/beans.xml")) {
result &= tree.apply(beansXml, pathVisit -> {
if (pathVisit == null) {
return true;
}

// crude but enough
try {
String text = Files.readString(pathVisit.getPath());
if (text.contains("bean-discovery-mode='none'")
|| text.contains("bean-discovery-mode=\"none\"")) {
return false;
}

if (text.contains("bean-discovery-mode='all'")
|| text.contains("bean-discovery-mode=\"all\"")) {
LOGGER.warnf("Detected bean archive with bean discovery mode of 'all', "
+ "this is not portable in CDI Lite and is treated as 'annotated' in Quarkus! "
+ "Path to beans.xml: %s", pathVisit.getPath());
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}

return true;
});
}
return result;
});
}

private boolean isApplicationArchiveExcluded(ArcConfig config, List<ExcludeDependencyBuildItem> excludeDependencyBuildItems,
ApplicationArchive archive) {
if (archive.getKey() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,11 @@ private List<BeanInfo> findBeans(Collection<DotName> beanDefiningAnnotations, Li
continue;
}

if (beanClass.interfaceNames().contains(DotNames.BUILD_COMPATIBLE_EXTENSION)) {
// Skip build compatible extensions
continue;
}

boolean hasBeanDefiningAnnotation = false;
if (annotationStore.hasAnyAnnotation(beanClass, beanDefiningAnnotations)) {
hasBeanDefiningAnnotation = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1460,16 +1460,6 @@ void implementCreateForClassBean(ClassOutput classOutput, ClassCreator beanCreat

AssignableResultHandle instanceHandle;

List<Injection> methodInjections = new ArrayList<>();
List<Injection> fieldInjections = new ArrayList<>();
for (Injection injection : bean.getInjections()) {
if (injection.isField()) {
fieldInjections.add(injection);
} else if (injection.isMethod() && !injection.isConstructor()) {
methodInjections.add(injection);
}
}

ResultHandle postConstructsHandle = null;
ResultHandle aroundConstructsHandle = null;
Map<InterceptorInfo, ResultHandle> interceptorToWrap = new HashMap<>();
Expand Down Expand Up @@ -1651,96 +1641,97 @@ void implementCreateForClassBean(ClassOutput classOutput, ClassCreator beanCreat
}

// Perform field and initializer injections
for (Injection fieldInjection : fieldInjections) {
TryBlock tryBlock = create.tryBlock();
InjectionPointInfo injectionPoint = fieldInjection.injectionPoints.get(0);
ResultHandle providerSupplierHandle = tryBlock.readInstanceField(FieldDescriptor.of(beanCreator.getClassName(),
injectionPointToProviderSupplierField.get(injectionPoint), Supplier.class.getName()),
tryBlock.getThis());
ResultHandle providerHandle = tryBlock.invokeInterfaceMethod(
MethodDescriptors.SUPPLIER_GET, providerSupplierHandle);
ResultHandle childCtxHandle = tryBlock.invokeStaticMethod(MethodDescriptors.CREATIONAL_CTX_CHILD_CONTEXTUAL,
providerHandle, tryBlock.getMethodParam(0));
AssignableResultHandle referenceHandle = tryBlock.createVariable(Object.class);
tryBlock.assign(referenceHandle, tryBlock.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET,
providerHandle, childCtxHandle));
checkPrimitiveInjection(tryBlock, injectionPoint, referenceHandle);

FieldInfo injectedField = fieldInjection.target.asField();
// only use reflection fallback if we are not performing transformation
if (isReflectionFallbackNeeded(injectedField, targetPackage, bean)) {
if (Modifier.isPrivate(injectedField.flags())) {
privateMembers.add(isApplicationClass,
String.format("@Inject field %s#%s", fieldInjection.target.asField().declaringClass().name(),
fieldInjection.target.asField().name()));
}
reflectionRegistration.registerField(injectedField);
tryBlock.invokeStaticMethod(MethodDescriptors.REFLECTIONS_WRITE_FIELD,
tryBlock.loadClass(injectedField.declaringClass().name().toString()),
tryBlock.load(injectedField.name()), instanceHandle, referenceHandle);

} else {
// We cannot use injectionPoint.getRequiredType() because it might be a resolved parameterize type and we could get NoSuchFieldError
tryBlock.writeInstanceField(
FieldDescriptor.of(injectedField.declaringClass().name().toString(), injectedField.name(),
DescriptorUtils.typeToString(injectionPoint.getTarget().asField().type())),
instanceHandle, referenceHandle);
}
CatchBlockCreator catchBlock = tryBlock.addCatch(RuntimeException.class);
catchBlock.throwException(RuntimeException.class, "Error injecting " + fieldInjection.target,
catchBlock.getCaughtException());
}
for (Injection methodInjection : methodInjections) {
List<TransientReference> transientReferences = new ArrayList<>();
ResultHandle[] referenceHandles = new ResultHandle[methodInjection.injectionPoints.size()];
int paramIdx = 0;
for (InjectionPointInfo injectionPoint : methodInjection.injectionPoints) {
ResultHandle providerSupplierHandle = create.readInstanceField(
FieldDescriptor.of(beanCreator.getClassName(),
injectionPointToProviderSupplierField.get(injectionPoint), Supplier.class.getName()),
create.getThis());
ResultHandle providerHandle = create.invokeInterfaceMethod(MethodDescriptors.SUPPLIER_GET,
providerSupplierHandle);
ResultHandle childCtxHandle = create.invokeStaticMethod(MethodDescriptors.CREATIONAL_CTX_CHILD_CONTEXTUAL,
providerHandle, create.getMethodParam(0));
AssignableResultHandle referenceHandle = create.createVariable(Object.class);
create.assign(referenceHandle, create.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET,
for (Injection injection : bean.getInjections()) {
if (injection.isField()) {
TryBlock tryBlock = create.tryBlock();
InjectionPointInfo injectionPoint = injection.injectionPoints.get(0);
ResultHandle providerSupplierHandle = tryBlock.readInstanceField(FieldDescriptor.of(beanCreator.getClassName(),
injectionPointToProviderSupplierField.get(injectionPoint), Supplier.class.getName()),
tryBlock.getThis());
ResultHandle providerHandle = tryBlock.invokeInterfaceMethod(
MethodDescriptors.SUPPLIER_GET, providerSupplierHandle);
ResultHandle childCtxHandle = tryBlock.invokeStaticMethod(MethodDescriptors.CREATIONAL_CTX_CHILD_CONTEXTUAL,
providerHandle, tryBlock.getMethodParam(0));
AssignableResultHandle referenceHandle = tryBlock.createVariable(Object.class);
tryBlock.assign(referenceHandle, tryBlock.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET,
providerHandle, childCtxHandle));
checkPrimitiveInjection(create, injectionPoint, referenceHandle);
referenceHandles[paramIdx++] = referenceHandle;
// We need to destroy dependent beans for @TransientReference injection points
if (injectionPoint.isDependentTransientReference()) {
transientReferences.add(new TransientReference(providerHandle, referenceHandle, childCtxHandle));
}
}
checkPrimitiveInjection(tryBlock, injectionPoint, referenceHandle);

FieldInfo injectedField = injection.target.asField();
// only use reflection fallback if we are not performing transformation
if (isReflectionFallbackNeeded(injectedField, targetPackage, bean)) {
if (Modifier.isPrivate(injectedField.flags())) {
privateMembers.add(isApplicationClass,
String.format("@Inject field %s#%s", injection.target.asField().declaringClass().name(),
injection.target.asField().name()));
}
reflectionRegistration.registerField(injectedField);
tryBlock.invokeStaticMethod(MethodDescriptors.REFLECTIONS_WRITE_FIELD,
tryBlock.loadClass(injectedField.declaringClass().name().toString()),
tryBlock.load(injectedField.name()), instanceHandle, referenceHandle);

MethodInfo initializerMethod = methodInjection.target.asMethod();
if (isReflectionFallbackNeeded(initializerMethod, targetPackage)) {
if (Modifier.isPrivate(initializerMethod.flags())) {
privateMembers.add(isApplicationClass,
String.format("@Inject initializer %s#%s()", initializerMethod.declaringClass().name(),
initializerMethod.name()));
} else {
// We cannot use injectionPoint.getRequiredType() because it might be a resolved parameterize type and we could get NoSuchFieldError
tryBlock.writeInstanceField(
FieldDescriptor.of(injectedField.declaringClass().name().toString(), injectedField.name(),
DescriptorUtils.typeToString(injectionPoint.getTarget().asField().type())),
instanceHandle, referenceHandle);
}
ResultHandle paramTypesArray = create.newArray(Class.class, create.load(referenceHandles.length));
ResultHandle argsArray = create.newArray(Object.class, create.load(referenceHandles.length));
for (int i = 0; i < referenceHandles.length; i++) {
create.writeArrayValue(paramTypesArray, i,
create.loadClass(initializerMethod.parameterType(i).name().toString()));
create.writeArrayValue(argsArray, i, referenceHandles[i]);
CatchBlockCreator catchBlock = tryBlock.addCatch(RuntimeException.class);
catchBlock.throwException(RuntimeException.class, "Error injecting " + injection.target,
catchBlock.getCaughtException());
} else if (injection.isMethod() && !injection.isConstructor()) {
List<TransientReference> transientReferences = new ArrayList<>();
ResultHandle[] referenceHandles = new ResultHandle[injection.injectionPoints.size()];
int paramIdx = 0;
for (InjectionPointInfo injectionPoint : injection.injectionPoints) {
ResultHandle providerSupplierHandle = create.readInstanceField(
FieldDescriptor.of(beanCreator.getClassName(),
injectionPointToProviderSupplierField.get(injectionPoint), Supplier.class.getName()),
create.getThis());
ResultHandle providerHandle = create.invokeInterfaceMethod(MethodDescriptors.SUPPLIER_GET,
providerSupplierHandle);
ResultHandle childCtxHandle = create.invokeStaticMethod(MethodDescriptors.CREATIONAL_CTX_CHILD_CONTEXTUAL,
providerHandle, create.getMethodParam(0));
AssignableResultHandle referenceHandle = create.createVariable(Object.class);
create.assign(referenceHandle, create.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET,
providerHandle, childCtxHandle));
checkPrimitiveInjection(create, injectionPoint, referenceHandle);
referenceHandles[paramIdx++] = referenceHandle;
// We need to destroy dependent beans for @TransientReference injection points
if (injectionPoint.isDependentTransientReference()) {
transientReferences.add(new TransientReference(providerHandle, referenceHandle, childCtxHandle));
}
}
reflectionRegistration.registerMethod(initializerMethod);
create.invokeStaticMethod(MethodDescriptors.REFLECTIONS_INVOKE_METHOD,
create.loadClass(initializerMethod.declaringClass().name().toString()),
create.load(methodInjection.target.asMethod().name()),
paramTypesArray, instanceHandle, argsArray);

} else {
create.invokeVirtualMethod(MethodDescriptor.of(methodInjection.target.asMethod()), instanceHandle,
referenceHandles);
}
MethodInfo initializerMethod = injection.target.asMethod();
if (isReflectionFallbackNeeded(initializerMethod, targetPackage)) {
if (Modifier.isPrivate(initializerMethod.flags())) {
privateMembers.add(isApplicationClass,
String.format("@Inject initializer %s#%s()", initializerMethod.declaringClass().name(),
initializerMethod.name()));
}
ResultHandle paramTypesArray = create.newArray(Class.class, create.load(referenceHandles.length));
ResultHandle argsArray = create.newArray(Object.class, create.load(referenceHandles.length));
for (int i = 0; i < referenceHandles.length; i++) {
create.writeArrayValue(paramTypesArray, i,
create.loadClass(initializerMethod.parameterType(i).name().toString()));
create.writeArrayValue(argsArray, i, referenceHandles[i]);
}
reflectionRegistration.registerMethod(initializerMethod);
create.invokeStaticMethod(MethodDescriptors.REFLECTIONS_INVOKE_METHOD,
create.loadClass(initializerMethod.declaringClass().name().toString()),
create.load(injection.target.asMethod().name()),
paramTypesArray, instanceHandle, argsArray);

// Destroy injected transient references
destroyTransientReferences(create, transientReferences);
} else {
create.invokeVirtualMethod(MethodDescriptor.of(injection.target.asMethod()), instanceHandle,
referenceHandles);
}

// Destroy injected transient references
destroyTransientReferences(create, transientReferences);
}
}

// PostConstruct lifecycle callback interceptors
Expand Down
Loading