Skip to content

Commit

Permalink
Extend reflection configuration for VertX
Browse files Browse the repository at this point in the history
Doesn't seem to fix any functionality issues but prevents
`MissingRegistrationErrors` from being thrown when
`ThrowMissingRegistrationErrors` is enabled.

Related to quarkusio#41995
  • Loading branch information
zakkak committed Aug 1, 2024
1 parent a184b98 commit 39090e8
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package io.quarkus.smallrye.faulttolerance.deployment;

import java.time.temporal.ChronoUnit;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.Queue;
import java.util.Set;

import jakarta.annotation.Priority;
Expand Down Expand Up @@ -88,7 +86,6 @@ public void build(BuildProducer<AnnotationsTransformerBuildItem> annotationsTran
BuildProducer<SystemPropertyBuildItem> systemProperty,
CombinedIndexBuildItem combinedIndexBuildItem,
BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
BuildProducer<ReflectiveMethodBuildItem> reflectiveMethod,
BuildProducer<RunTimeConfigurationDefaultBuildItem> config,
BuildProducer<RuntimeInitializedClassBuildItem> runtimeInitializedClassBuildItems) {

Expand Down Expand Up @@ -120,43 +117,6 @@ public void build(BuildProducer<AnnotationsTransformerBuildItem> annotationsTran
}
beans.produce(handlerBeans.build());
}
// Add reflective access to fallback methods
for (AnnotationInstance annotation : index.getAnnotations(DotNames.FALLBACK)) {
AnnotationValue fallbackMethodValue = annotation.value("fallbackMethod");
if (fallbackMethodValue == null) {
continue;
}
String fallbackMethod = fallbackMethodValue.asString();

Queue<DotName> classesToScan = new ArrayDeque<>(); // work queue

// @Fallback can only be present on methods, so this is just future-proofing
AnnotationTarget target = annotation.target();
if (target.kind() == Kind.METHOD) {
classesToScan.add(target.asMethod().declaringClass().name());
}

while (!classesToScan.isEmpty()) {
DotName name = classesToScan.poll();
ClassInfo clazz = index.getClassByName(name);
if (clazz == null) {
continue;
}

// we could further restrict the set of registered methods based on matching parameter types,
// but that's relatively complex and SmallRye Fault Tolerance has to do it anyway
clazz.methods()
.stream()
.filter(it -> fallbackMethod.equals(it.name()))
.forEach(it -> reflectiveMethod.produce(new ReflectiveMethodBuildItem(it)));

DotName superClass = clazz.superName();
if (superClass != null && !DotNames.OBJECT.equals(superClass)) {
classesToScan.add(superClass);
}
classesToScan.addAll(clazz.interfaceNames());
}
}
// Add reflective access to custom backoff strategies
for (ClassInfo strategy : index.getAllKnownImplementors(DotNames.CUSTOM_BACKOFF_STRATEGY)) {
reflectiveClass.produce(ReflectiveClassBuildItem.builder(strategy.name().toString()).methods().build());
Expand Down Expand Up @@ -260,6 +220,67 @@ public void transform(TransformationContext ctx) {
});
}

@BuildStep
// Add reflective access to fallback methods, use bean index since fallback methods can be in bean classes
void processFallbackMethodsAndClases(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem,
BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
BuildProducer<ReflectiveMethodBuildItem> reflectiveMethod) {
IndexView index = beanArchiveIndexBuildItem.getIndex();
Map<DotName, Set<String>> classesToScan = new HashMap<>();

for (AnnotationInstance annotation : index.getAnnotations(DotNames.FALLBACK)) {
AnnotationValue fallbackMethodValue = annotation.value("fallbackMethod");
if (fallbackMethodValue == null) {
continue;
}
String fallbackMethod = fallbackMethodValue.asString();

// @Fallback can only be present on methods, so this is just future-proofing
AnnotationTarget target = annotation.target();
final ClassInfo clazz = target.asMethod().declaringClass();
if (target.kind() != Kind.METHOD) {
continue;
}

// Scan both the hierarchy of the declaring class and its interfaces like in
// io.smallrye.faulttolerance.internal.SecurityActions.findDeclaredMethodNames
DotName name = clazz.name();
while (name != null && !DotNames.OBJECT.equals(name)) {
Set<String> methods = classesToScan.computeIfAbsent(name, k -> new HashSet<>());
methods.add(fallbackMethod);
ClassInfo classInfo = index.getClassByName(name);
if (classInfo == null) {
break;
}
name = classInfo.superName();
}
clazz.interfaceNames().forEach(it -> classesToScan.computeIfAbsent(it, k -> new HashSet<>()).add(fallbackMethod));
}

for (Map.Entry<DotName, Set<String>> entry : classesToScan.entrySet()) {
DotName name = entry.getKey();
Set<String> methods = entry.getValue();
ClassInfo classInfo = index.getClassByName(name);

// io.smallrye.faulttolerance.config.FaultToleranceOperation.validate reflectively accesses methods and
// interfaces of the bean class and its superclasses through
// io.smallrye.faulttolerance.internal.SecurityActions.findDeclaredMethodNames
if (classInfo.isInterface()) {
// fot interfaces getMethods() is invoked in addition to getDeclaredMethods()
reflectiveClass.produce(ReflectiveClassBuildItem.builder(name.toString()).queryPublicMethods().build());
}
reflectiveClass.produce(ReflectiveClassBuildItem.builder(name.toString()).queryMethods().build());

// we could further restrict the set of registered methods based on matching parameter types,
// but that's relatively complex and SmallRye Fault Tolerance has to do it anyway
classInfo.methods()
.stream()
.filter(it -> methods.contains(it.name()))
.forEach(it -> reflectiveMethod.produce(new ReflectiveMethodBuildItem(it)));
}

}

@BuildStep
// needs to be RUNTIME_INIT because we need to read MP Config
@Record(ExecutionTime.RUNTIME_INIT)
Expand Down Expand Up @@ -287,6 +308,7 @@ void processFaultToleranceAnnotations(SmallRyeFaultToleranceRecorder recorder,

AnnotationStore annotationStore = validationPhase.getContext().get(BuildExtension.Key.ANNOTATION_STORE);
IndexView index = beanArchiveIndexBuildItem.getIndex();

// only generating annotation literal classes for MicroProfile/SmallRye Fault Tolerance annotations,
// none of them are application classes
ClassOutput classOutput = new GeneratedClassGizmoAdaptor(generatedClasses, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@
import io.quarkus.deployment.builditem.ServiceStartBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageConfigBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveMethodBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem;
import io.quarkus.deployment.recording.RecorderContext;
import io.quarkus.gizmo.ClassOutput;
Expand Down Expand Up @@ -274,4 +276,20 @@ private Class<?> tryLoad(String name, ClassLoader tccl) {
throw new IllegalStateException("Unable to load type: " + name, e);
}
}

@BuildStep
void registerNativeImageResources(BuildProducer<NativeImageResourceBuildItem> resources) {
// Accessed by io.vertx.core.impl.VertxBuilder.<init>
resources.produce(new NativeImageResourceBuildItem("META-INF/services/io.vertx.core.spi.VertxServiceProvider"));
// Accessed by io.vertx.core.impl.VertxImpl.<init>
resources.produce(new NativeImageResourceBuildItem("META-INF/services/io.vertx.core.spi.VerticleFactory"));
}

@BuildStep
void registerReflectivelyAccessedMethods(BuildProducer<ReflectiveMethodBuildItem> reflectiveMethods) {
// Accessed by io.vertx.core.impl.VertxImpl.<init>
reflectiveMethods.produce(new ReflectiveMethodBuildItem("java.lang.Thread$Builder$OfVirtual", "name",
String.class, long.class));
reflectiveMethods.produce(new ReflectiveMethodBuildItem("java.lang.Thread$Builder", "factory", new Class[0]));
}
}

0 comments on commit 39090e8

Please sign in to comment.