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 #41995
  • Loading branch information
zakkak committed Jul 31, 2024
1 parent a02d772 commit ee25435
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,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 +119,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 @@ -270,6 +232,7 @@ void processFaultToleranceAnnotations(SmallRyeFaultToleranceRecorder recorder,
AnnotationProxyBuildItem annotationProxy,
BuildProducer<GeneratedClassBuildItem> generatedClasses,
BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
BuildProducer<ReflectiveMethodBuildItem> reflectiveMethod,
BuildProducer<ValidationPhaseBuildItem.ValidationErrorBuildItem> errors,
BuildProducer<FaultToleranceInfoBuildItem> faultToleranceInfo) {

Expand All @@ -287,6 +250,49 @@ void processFaultToleranceAnnotations(SmallRyeFaultToleranceRecorder recorder,

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

// 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();

System.out.println("Looking for fallback method: " + fallbackMethod);

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;
}

System.out.println("Looking into class: " + clazz);

// 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());
}
}

// only generating annotation literal classes for MicroProfile/SmallRye Fault Tolerance annotations,
// none of them are application classes
ClassOutput classOutput = new GeneratedClassGizmoAdaptor(generatedClasses, false);
Expand All @@ -313,6 +319,9 @@ void processFaultToleranceAnnotations(SmallRyeFaultToleranceRecorder recorder,
}
}

// io.smallrye.faulttolerance.internal.SecurityActions.findDeclaredMethodNames accesses class' methods
reflectiveClass.produce(ReflectiveClassBuildItem.builder(beanClass.name().toString()).queryMethods().build());

scanner.forEachMethod(beanClass, method -> {
FaultToleranceMethod ftMethod = scanner.createFaultToleranceMethod(beanClass, method);
if (ftMethod.isLegitimate()) {
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 ee25435

Please sign in to comment.