Skip to content

Commit

Permalink
Merge pull request #44921 from radcortez/converters-reflection
Browse files Browse the repository at this point in the history
Avoid Converters annotations introspection at runtime
  • Loading branch information
radcortez authored Dec 5, 2024
2 parents fa7bb99 + 8b82767 commit f66bb9e
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,19 @@
import java.util.Optional;
import java.util.Set;

import jakarta.annotation.Priority;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.config.ConfigValue;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.eclipse.microprofile.config.spi.ConfigSourceProvider;
import org.eclipse.microprofile.config.spi.Converter;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.ParameterizedType;
import org.jboss.jandex.Type;
import org.objectweb.asm.Opcodes;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
Expand Down Expand Up @@ -200,6 +207,7 @@ void generateMappings(
@BuildStep
void generateBuilders(
ConfigurationBuildItem configItem,
CombinedIndexBuildItem combinedIndex,
List<ConfigMappingBuildItem> configMappings,
List<RunTimeConfigurationDefaultBuildItem> runTimeDefaults,
List<StaticInitConfigBuilderBuildItem> staticInitConfigBuilders,
Expand Down Expand Up @@ -246,6 +254,7 @@ void generateBuilders(
staticCustomizers.add(StaticInitConfigBuilder.class.getName());

generateConfigBuilder(generatedClass, reflectiveClass, CONFIG_STATIC_NAME,
combinedIndex,
defaultValues,
converters,
interceptors,
Expand All @@ -269,6 +278,7 @@ void generateBuilders(
runtimeCustomizers.add(RuntimeConfigBuilder.class.getName());

generateConfigBuilder(generatedClass, reflectiveClass, CONFIG_RUNTIME_NAME,
combinedIndex,
defaultValues,
converters,
interceptors,
Expand Down Expand Up @@ -520,7 +530,7 @@ private static String getPathWithoutExtension(Path path) {
"withDefaultValue",
void.class, SmallRyeConfigBuilder.class, String.class, String.class);
private static final MethodDescriptor WITH_CONVERTER = MethodDescriptor.ofMethod(AbstractConfigBuilder.class,
"withConverter", void.class, SmallRyeConfigBuilder.class, Converter.class);
"withConverter", void.class, SmallRyeConfigBuilder.class, String.class, int.class, Converter.class);
private static final MethodDescriptor WITH_INTERCEPTOR = MethodDescriptor.ofMethod(AbstractConfigBuilder.class,
"withInterceptor",
void.class, SmallRyeConfigBuilder.class, ConfigSourceInterceptor.class);
Expand Down Expand Up @@ -549,17 +559,15 @@ private static String getPathWithoutExtension(Path path) {
private static final MethodDescriptor WITH_BUILDER = MethodDescriptor.ofMethod(AbstractConfigBuilder.class,
"withBuilder",
void.class, SmallRyeConfigBuilder.class, ConfigBuilder.class);
private static final MethodDescriptor WITH_NAMES = MethodDescriptor.ofMethod(SmallRyeConfigBuilder.class,
"withMappingNames",
SmallRyeConfigBuilder.class, Map.class);
private static final MethodDescriptor WITH_KEYS = MethodDescriptor.ofMethod(SmallRyeConfigBuilder.class,
"withMappingKeys",
SmallRyeConfigBuilder.class, Set.class);

private static final DotName CONVERTER_NAME = DotName.createSimple(Converter.class.getName());
private static final DotName PRIORITY_NAME = DotName.createSimple(Priority.class.getName());

private static void generateConfigBuilder(
BuildProducer<GeneratedClassBuildItem> generatedClass,
BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
String className,
CombinedIndexBuildItem combinedIndex,
Map<String, String> defaultValues,
Set<String> converters,
Set<String> interceptors,
Expand Down Expand Up @@ -591,7 +599,13 @@ private static void generateConfigBuilder(
}

for (String converter : converters) {
ClassInfo converterClass = combinedIndex.getComputingIndex().getClassByName(converter);
Type type = getConverterType(converterClass, combinedIndex);
AnnotationInstance priorityAnnotation = converterClass.annotation(PRIORITY_NAME);
int priority = priorityAnnotation != null ? priorityAnnotation.value().asInt() : 100;
method.invokeStaticMethod(WITH_CONVERTER, configBuilder,
method.load(type.name().toString()),
method.load(priority),
method.newInstance(MethodDescriptor.ofConstructor(converter)));
}

Expand Down Expand Up @@ -716,4 +730,27 @@ private static Set<ConfigClass> runtimeConfigMappings(List<ConfigMappingBuildIte
.map(ConfigMappingBuildItem::toConfigClass)
.collect(toSet());
}

private static Type getConverterType(final ClassInfo converter, final CombinedIndexBuildItem combinedIndex) {
if (converter.name().toString().equals(Object.class.getName())) {
throw new IllegalArgumentException(
"Can not add converter " + converter.name() + " that is not parameterized with a type");
}

for (Type type : converter.interfaceTypes()) {
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = type.asParameterizedType();
if (parameterizedType.name().equals(CONVERTER_NAME)) {
List<Type> arguments = parameterizedType.arguments();
if (arguments.size() != 1) {
throw new IllegalArgumentException(
"Converter " + converter.name() + " must be parameterized with a single type");
}
return arguments.get(0);
}
}
}

return getConverterType(combinedIndex.getComputingIndex().getClassByName(converter.superName()), combinedIndex);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ protected static void withDefaultValue(SmallRyeConfigBuilder builder, String nam
builder.withDefaultValue(name, value);
}

// TODO - radcortez - Can be improved by avoiding introspection work in the Converter class.
// Not a big issue, because registering Converters via ServiceLoader is not a common case
protected static void withConverter(SmallRyeConfigBuilder builder, Converter<?> converter) {
builder.withConverters(new Converter[] { converter });
@SuppressWarnings("unchecked")
protected static <T> void withConverter(SmallRyeConfigBuilder builder, String type, int priority, Converter<T> converter) {
try {
// To support converters that are not public
builder.withConverter((Class<T>) builder.getClassLoader().loadClass(type), priority, converter);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}

protected static void withInterceptor(SmallRyeConfigBuilder builder, ConfigSourceInterceptor interceptor) {
Expand Down

0 comments on commit f66bb9e

Please sign in to comment.