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

Refactor BuildTimeConfigurationReader to make it easy to initialize a config outside ExtensionLoader #34449

Merged
merged 1 commit into from
Jul 3, 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 @@ -16,32 +16,29 @@
import java.util.function.Consumer;

import org.eclipse.microprofile.config.Config;
import org.jboss.logging.Logger;

import io.quarkus.bootstrap.classloading.MemoryClassPathElement;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.bootstrap.model.ApplicationModel;
import io.quarkus.bootstrap.prebuild.CodeGenException;
import io.quarkus.deployment.codegen.CodeGenData;
import io.quarkus.deployment.configuration.BuildTimeConfigurationReader;
import io.quarkus.deployment.dev.DevModeContext;
import io.quarkus.deployment.dev.DevModeContext.ModuleInfo;
import io.quarkus.maven.dependency.ResolvedDependency;
import io.quarkus.paths.OpenPathTree;
import io.quarkus.paths.PathCollection;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.configuration.ConfigUtils;
import io.quarkus.runtime.util.ClassPathUtils;
import io.smallrye.config.KeyMap;
import io.smallrye.config.KeyMapBackedConfigSource;
import io.smallrye.config.NameIterator;
import io.smallrye.config.PropertiesConfigSource;
import io.smallrye.config.SmallRyeConfigBuilder;
import io.smallrye.config.SysPropConfigSource;

/**
* A set of methods to initialize and execute {@link CodeGenProvider}s.
*/
public class CodeGenerator {

private static final Logger log = Logger.getLogger(CodeGenerator.class);

private static final List<String> CONFIG_SOURCE_FACTORY_INTERFACES = List.of(
"META-INF/services/io.smallrye.config.ConfigSourceFactory",
"META-INF/services/org.eclipse.microprofile.config.spi.ConfigSourceProvider");
Expand Down Expand Up @@ -160,7 +157,7 @@ private static <T> T callWithClassloader(ClassLoader deploymentClassLoader, Code
* @param deploymentClassLoader deployment classloader
* @param data code gen
* @param appModel app model
* @param properties custom code generation properties
* @param config config instance
* @param test whether the sources are generated for production code or tests
* @return true if sources have been created
* @throws CodeGenException on failure
Expand Down Expand Up @@ -207,13 +204,13 @@ public static Config getConfig(ApplicationModel appModel, LaunchMode launchMode,
bannedConfigFactories.put(factoryImpl, new byte[0]);
} else {
final StringJoiner joiner = new StringJoiner(System.lineSeparator());
allFactories.forEach(f -> joiner.add(f));
allFactories.forEach(joiner::add);
allowedConfigFactories.put(factoryImpl, joiner.toString().getBytes());
}
}

// we don't want to load config source factories/providers from the current module because they haven't been compiled yet
QuarkusClassLoader.Builder configClBuilder = QuarkusClassLoader.builder("CodeGenerator Config ClassLoader",
final QuarkusClassLoader.Builder configClBuilder = QuarkusClassLoader.builder("CodeGenerator Config ClassLoader",
deploymentClassLoader, false);
if (!allowedConfigFactories.isEmpty()) {
configClBuilder.addElement(new MemoryClassPathElement(allowedConfigFactories, true));
Expand All @@ -224,26 +221,10 @@ public static Config getConfig(ApplicationModel appModel, LaunchMode launchMode,
deploymentClassLoader = configClBuilder.build();
}
try {
final SmallRyeConfigBuilder builder = ConfigUtils.configBuilder(false, launchMode)
.forClassLoader(deploymentClassLoader);
final PropertiesConfigSource pcs = new PropertiesConfigSource(buildSystemProps, "Build system");
final SysPropConfigSource spcs = new SysPropConfigSource();

final Map<String, String> platformProperties = appModel.getPlatformProperties();
if (platformProperties.isEmpty()) {
builder.withSources(pcs, spcs);
} else {
final KeyMap<String> props = new KeyMap<>(platformProperties.size());
for (Map.Entry<String, String> prop : platformProperties.entrySet()) {
props.findOrAdd(new NameIterator(prop.getKey())).putRootValue(prop.getValue());
}
final KeyMapBackedConfigSource platformConfigSource = new KeyMapBackedConfigSource("Quarkus platform",
// Our default value configuration source is using an ordinal of Integer.MIN_VALUE
// (see io.quarkus.deployment.configuration.DefaultValuesConfigurationSource)
Integer.MIN_VALUE + 1000, props);
builder.withSources(platformConfigSource, pcs, spcs);
}
return builder.build();
return new BuildTimeConfigurationReader(deploymentClassLoader).initConfiguration(launchMode, buildSystemProps,
appModel.getPlatformProperties());
} catch (Exception e) {
throw new CodeGenException("Failed to initialize application configuration", e);
} finally {
if (!appModuleConfigFactories.isEmpty()) {
deploymentClassLoader.close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@
import io.quarkus.deployment.builditem.StaticBytecodeRecorderBuildItem;
import io.quarkus.deployment.configuration.BuildTimeConfigurationReader;
import io.quarkus.deployment.configuration.ConfigMappingUtils;
import io.quarkus.deployment.configuration.DefaultValuesConfigurationSource;
import io.quarkus.deployment.configuration.definition.RootDefinition;
import io.quarkus.deployment.recording.BytecodeRecorderImpl;
import io.quarkus.deployment.recording.ObjectLoader;
Expand All @@ -99,16 +98,10 @@
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.quarkus.runtime.annotations.Recorder;
import io.quarkus.runtime.configuration.ConfigUtils;
import io.quarkus.runtime.configuration.QuarkusConfigFactory;
import io.quarkus.runtime.util.HashUtil;
import io.smallrye.config.ConfigMappings.ConfigClassWithPrefix;
import io.smallrye.config.KeyMap;
import io.smallrye.config.KeyMapBackedConfigSource;
import io.smallrye.config.NameIterator;
import io.smallrye.config.PropertiesConfigSource;
import io.smallrye.config.SmallRyeConfig;
import io.smallrye.config.SmallRyeConfigBuilder;

/**
* Utility class to load build steps, runtime recorders, and configuration roots from a given extension class.
Expand All @@ -120,7 +113,6 @@ private ExtensionLoader() {

private static final Logger loadLog = Logger.getLogger("io.quarkus.deployment");
private static final Logger cfgLog = Logger.getLogger("io.quarkus.configuration");
private static final String CONFIG_ROOTS_LIST = "META-INF/quarkus-config-roots.list";
@SuppressWarnings("unchecked")
private static final Class<? extends BooleanSupplier>[] EMPTY_BOOLEAN_SUPPLIER_CLASS_ARRAY = new Class[0];

Expand All @@ -142,46 +134,9 @@ private static boolean isRecorder(AnnotatedElement element) {
public static Consumer<BuildChainBuilder> loadStepsFrom(ClassLoader classLoader, Properties buildSystemProps,
ApplicationModel appModel, LaunchMode launchMode, DevModeType devModeType)
throws IOException, ClassNotFoundException {
// populate with all known types
List<Class<?>> roots = new ArrayList<>();
for (Class<?> clazz : ServiceUtil.classesNamedIn(classLoader, CONFIG_ROOTS_LIST)) {
final ConfigRoot annotation = clazz.getAnnotation(ConfigRoot.class);
if (annotation == null) {
cfgLog.warnf("Ignoring configuration root %s because it has no annotation", clazz);
} else {
roots.add(clazz);
}
}

final BuildTimeConfigurationReader reader = new BuildTimeConfigurationReader(roots);

// now prepare & load the build configuration
final SmallRyeConfigBuilder builder = ConfigUtils.configBuilder(false, launchMode);

final DefaultValuesConfigurationSource ds1 = new DefaultValuesConfigurationSource(
reader.getBuildTimePatternMap());
final DefaultValuesConfigurationSource ds2 = new DefaultValuesConfigurationSource(
reader.getBuildTimeRunTimePatternMap());
final PropertiesConfigSource pcs = new PropertiesConfigSource(buildSystemProps, "Build system");
final Map<String, String> platformProperties = appModel.getPlatformProperties();
if (platformProperties.isEmpty()) {
builder.withSources(ds1, ds2, pcs);
} else {
final KeyMap<String> props = new KeyMap<>(platformProperties.size());
for (Map.Entry<String, String> prop : platformProperties.entrySet()) {
props.findOrAdd(new NameIterator(prop.getKey())).putRootValue(prop.getValue());
}
final KeyMapBackedConfigSource platformConfigSource = new KeyMapBackedConfigSource("Quarkus platform",
// Our default value configuration source is using an ordinal of Integer.MIN_VALUE
// (see io.quarkus.deployment.configuration.DefaultValuesConfigurationSource)
Integer.MIN_VALUE + 1000, props);
builder.withSources(ds1, ds2, platformConfigSource, pcs);
}

for (ConfigClassWithPrefix mapping : reader.getBuildTimeVisibleMappings()) {
builder.withMapping(mapping.getKlass(), mapping.getPrefix());
}
final SmallRyeConfig src = builder.build();
final BuildTimeConfigurationReader reader = new BuildTimeConfigurationReader(classLoader);
final SmallRyeConfig src = reader.initConfiguration(launchMode, buildSystemProps, appModel.getPlatformProperties());

// install globally
QuarkusConfigFactory.setConfig(src);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import static io.smallrye.config.Expressions.withoutExpansion;
import static java.util.stream.Collectors.toSet;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
Expand All @@ -23,6 +24,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
Expand Down Expand Up @@ -54,6 +56,8 @@
import io.quarkus.deployment.configuration.type.OptionalOf;
import io.quarkus.deployment.configuration.type.PatternValidated;
import io.quarkus.deployment.configuration.type.UpperBoundCheckOf;
import io.quarkus.deployment.util.ServiceUtil;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
Expand All @@ -69,6 +73,9 @@
import io.smallrye.config.ConfigValue;
import io.smallrye.config.Converters;
import io.smallrye.config.EnvConfigSource;
import io.smallrye.config.KeyMap;
import io.smallrye.config.KeyMapBackedConfigSource;
import io.smallrye.config.PropertiesConfigSource;
import io.smallrye.config.SecretKeys;
import io.smallrye.config.SmallRyeConfig;
import io.smallrye.config.SmallRyeConfigBuilder;
Expand All @@ -80,6 +87,25 @@
public final class BuildTimeConfigurationReader {
private static final Logger log = Logger.getLogger("io.quarkus.config.build");

private static final String CONFIG_ROOTS_LIST = "META-INF/quarkus-config-roots.list";

private static List<Class<?>> collectConfigRoots(ClassLoader classLoader) throws IOException, ClassNotFoundException {
Assert.checkNotNullParam("classLoader", classLoader);
// populate with all known types
List<Class<?>> roots = new ArrayList<>();
for (Class<?> clazz : ServiceUtil.classesNamedIn(classLoader, CONFIG_ROOTS_LIST)) {
final ConfigRoot annotation = clazz.getAnnotation(ConfigRoot.class);
if (annotation == null) {
log.warnf("Ignoring configuration root %s because it has no annotation", clazz);
} else {
roots.add(clazz);
}
}
return roots;
}

final ClassLoader classLoader;

final ConfigPatternMap<Container> buildTimePatternMap;
final ConfigPatternMap<Container> buildTimeRunTimePatternMap;
final ConfigPatternMap<Container> runTimePatternMap;
Expand All @@ -98,13 +124,31 @@ public final class BuildTimeConfigurationReader {
final Set<String> deprecatedProperties;
final Set<String> deprecatedRuntimeProperties;

/**
* Initializes a new instance with located configuration root classes on the classpath
* of a given classloader.
*
* @param classLoader class loader to load configuration root classes from
* @throws IOException in case a classpath resource couldn't be read
* @throws ClassNotFoundException in case a config root class could not be found
*/
public BuildTimeConfigurationReader(ClassLoader classLoader) throws IOException, ClassNotFoundException {
this(classLoader, collectConfigRoots(classLoader));
}

/**
* Construct a new instance.
*
* @param configRoots the configuration root class list (must not be {@code null})
*/
public BuildTimeConfigurationReader(final List<Class<?>> configRoots) {
this(null, configRoots);
}

private BuildTimeConfigurationReader(ClassLoader classLoader, final List<Class<?>> configRoots) {

Assert.checkNotNullParam("configRoots", configRoots);
this.classLoader = classLoader;

List<RootDefinition> buildTimeRoots = new ArrayList<>();
List<RootDefinition> buildTimeRunTimeRoots = new ArrayList<>();
Expand Down Expand Up @@ -328,6 +372,45 @@ public List<ConfigClassWithPrefix> getBuildTimeVisibleMappings() {
return buildTimeVisibleMappings;
}

/**
* Builds a new configuration instance.
*
* @param launchMode target launch mode
* @param buildSystemProps build system properties to add as a configuration source
* @param platformProperties Quarkus platform properties to add as a configuration source
* @return configuration instance
*/
public SmallRyeConfig initConfiguration(LaunchMode launchMode, Properties buildSystemProps,
Map<String, String> platformProperties) {
// now prepare & load the build configuration
final SmallRyeConfigBuilder builder = ConfigUtils.configBuilder(false, launchMode);
if (classLoader != null) {
builder.forClassLoader(classLoader);
}

final DefaultValuesConfigurationSource ds1 = new DefaultValuesConfigurationSource(getBuildTimePatternMap());
final DefaultValuesConfigurationSource ds2 = new DefaultValuesConfigurationSource(getBuildTimeRunTimePatternMap());
final PropertiesConfigSource pcs = new PropertiesConfigSource(buildSystemProps, "Build system");
if (platformProperties.isEmpty()) {
builder.withSources(ds1, ds2, pcs);
} else {
final KeyMap<String> props = new KeyMap<>(platformProperties.size());
for (Map.Entry<String, String> prop : platformProperties.entrySet()) {
props.findOrAdd(new io.smallrye.config.NameIterator(prop.getKey())).putRootValue(prop.getValue());
}
final KeyMapBackedConfigSource platformConfigSource = new KeyMapBackedConfigSource("Quarkus platform",
// Our default value configuration source is using an ordinal of Integer.MIN_VALUE
// (see io.quarkus.deployment.configuration.DefaultValuesConfigurationSource)
Integer.MIN_VALUE + 1000, props);
builder.withSources(ds1, ds2, platformConfigSource, pcs);
}

for (ConfigClassWithPrefix mapping : getBuildTimeVisibleMappings()) {
builder.withMapping(mapping.getKlass(), mapping.getPrefix());
}
return builder.build();
}

public ReadResult readConfiguration(final SmallRyeConfig config) {
return SecretKeys.doUnlocked(() -> new ReadOperation(config).run());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.bootstrap.model.ApplicationModel;
import io.quarkus.maven.dependency.ArtifactCoords;
import io.quarkus.paths.PathCollection;
import io.quarkus.paths.PathList;
import io.quarkus.runtime.LaunchMode;
Expand All @@ -37,7 +38,7 @@ public class GenerateCodeMojo extends QuarkusBootstrapMojo {

@Override
protected boolean beforeExecute() throws MojoExecutionException, MojoFailureException {
if (mavenProject().getPackaging().equals("pom")) {
if (mavenProject().getPackaging().equals(ArtifactCoords.TYPE_POM)) {
getLog().info("Type of the artifact is POM, skipping build goal");
return false;
}
Expand Down Expand Up @@ -86,7 +87,7 @@ void generateCode(PathCollection sourceParents,
boolean.class);
initAndRun.invoke(null, deploymentClassLoader, sourceParents,
generatedSourcesDir(test), buildDir().toPath(),
sourceRegistrar, curatedApplication.getApplicationModel(), mavenProject().getProperties(),
sourceRegistrar, curatedApplication.getApplicationModel(), getBuildSystemProperties(false),
launchMode.name(),
test);
} catch (Exception any) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.maven.AbstractMavenLifecycleParticipant;
import org.apache.maven.execution.MavenSession;
Expand Down Expand Up @@ -135,7 +135,7 @@ public abstract class QuarkusBootstrapMojo extends AbstractMojo {
* The context of the execution of the plugin.
*/
@Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true)
private MojoExecution mojoExecution;
MojoExecution mojoExecution;

/**
* Application bootstrap provider ID. This parameter is not supposed to be configured by the user.
Expand Down Expand Up @@ -217,7 +217,7 @@ protected String appArtifactCoords() {
* @return list of extra dependencies that should be enforced on the application
*/
protected List<Dependency> forcedDependencies(LaunchMode mode) {
return Collections.emptyList();
return List.of();
}

@Deprecated(forRemoval = true)
Expand Down Expand Up @@ -289,4 +289,8 @@ protected CuratedApplication bootstrapApplication() throws MojoExecutionExceptio
protected CuratedApplication bootstrapApplication(LaunchMode mode) throws MojoExecutionException {
return bootstrapProvider.bootstrapApplication(this, mode);
}

protected Properties getBuildSystemProperties(boolean quarkusOnly) throws MojoExecutionException {
return bootstrapProvider.bootstrapper(this).getBuildSystemProperties(this, quarkusOnly);
}
}
Loading