diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java index a8d4b305e2fc4..1de333243329e 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java @@ -88,6 +88,7 @@ import io.quarkus.runtime.configuration.ConfigDiagnostic; import io.quarkus.runtime.configuration.ConfigRecorder; import io.quarkus.runtime.configuration.DisableableConfigSource; +import io.quarkus.runtime.configuration.NativeConfigBuilder; import io.quarkus.runtime.configuration.QuarkusConfigValue; import io.quarkus.runtime.configuration.RuntimeConfigBuilder; import io.quarkus.runtime.configuration.RuntimeOverrideConfigSource; @@ -168,6 +169,14 @@ void buildTimeRunTimeConfig( runTimeConfigBuilder.produce(new RunTimeConfigBuilderBuildItem(builderClassName)); } + @BuildStep(onlyIf = NativeOrNativeSourcesBuild.class) + void nativeConfig( + BuildProducer staticInitConfigBuilder, + BuildProducer runTimeConfigBuilder) { + staticInitConfigBuilder.produce(new StaticInitConfigBuilderBuildItem(NativeConfigBuilder.class.getName())); + runTimeConfigBuilder.produce(new RunTimeConfigBuilderBuildItem(NativeConfigBuilder.class.getName())); + } + @BuildStep(onlyIfNot = { IsNormal.class }) // for dev or test void runtimeOverrideConfig( BuildProducer staticInitConfigBuilder, diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/NativeConfigBuilder.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/NativeConfigBuilder.java new file mode 100644 index 0000000000000..c4ab5f93cbd58 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/NativeConfigBuilder.java @@ -0,0 +1,37 @@ +package io.quarkus.runtime.configuration; + +import static io.smallrye.config.PropertiesConfigSourceLoader.inFileSystem; + +import java.nio.file.Paths; + +import io.smallrye.config.SmallRyeConfigBuilder; + +/** + * Do not register classpath resources lookup in native mode to avoid missing resources registration errors, which + * became a strict check during native image execution. + *

+ * Configuration coming from classpath resources is recoded during build time in a low ordinal source, so the + * configuration will still be available. If the users change the ordinals of the sources in runtime, it may + * cause unexpected values to be returned by the config, but this has always been the case. The classpath configuration + * resources were never registered in the native image. + */ +public class NativeConfigBuilder implements ConfigBuilder { + @Override + public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) { + builder + .setAddDefaultSources(false) + .setAddSystemSources(true) + .setAddPropertiesSources(false); + + builder.withSources(inFileSystem( + Paths.get(System.getProperty("user.dir"), "config", "application.properties").toUri().toString(), 260, + builder.getClassLoader())); + + return builder; + } + + @Override + public int priority() { + return Integer.MIN_VALUE + 100; + } +} diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/Substitutions.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/Substitutions.java index 5b143025bee3d..c389328535d10 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/Substitutions.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/Substitutions.java @@ -1,6 +1,14 @@ package io.quarkus.runtime.configuration; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import org.eclipse.microprofile.config.spi.ConfigProviderResolver; +import org.eclipse.microprofile.config.spi.ConfigSource; +import org.eclipse.microprofile.config.spi.Converter; +import org.graalvm.nativeimage.MissingReflectionRegistrationError; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.RecomputeFieldValue; @@ -9,9 +17,11 @@ import com.oracle.svm.core.annotate.TargetElement; import io.smallrye.common.constraint.Assert; +import io.smallrye.config.AbstractLocationConfigSourceLoader; import io.smallrye.config.ConfigMappingInterface; import io.smallrye.config.ConfigMappingLoader; import io.smallrye.config.ConfigMappingMetadata; +import io.smallrye.config._private.ConfigMessages; /** */ @@ -99,4 +109,52 @@ public byte[] getClassBytes() { return null; } } + + @TargetClass(value = AbstractLocationConfigSourceLoader.class) + static final class Target_AbstractLocationConfigSourceLoader { + @Alias + protected native List tryClassPath(final URI uri, final int ordinal, final ClassLoader classLoader); + + @Alias + protected native List tryFileSystem(final URI uri, final int ordinal); + + @Alias + protected native List tryJar(final URI uri, final int ordinal); + + @Alias + protected native List tryHttpResource(final URI uri, final int ordinal); + + @Alias + private static Converter URI_CONVERTER = null; + + @Substitute + protected List loadConfigSources(final String[] locations, final int ordinal, + final ClassLoader classLoader) { + if (locations == null || locations.length == 0) { + return Collections.emptyList(); + } + + final List configSources = new ArrayList<>(); + for (String location : locations) { + final URI uri = URI_CONVERTER.convert(location); + if (uri.getScheme() == null) { + configSources.addAll(tryFileSystem(uri, ordinal)); + try { + configSources.addAll(tryClassPath(uri, ordinal, classLoader)); + } catch (MissingReflectionRegistrationError e) { + // ignore + } + } else if (uri.getScheme().equals("file")) { + configSources.addAll(tryFileSystem(uri, ordinal)); + } else if (uri.getScheme().equals("jar")) { + configSources.addAll(tryJar(uri, ordinal)); + } else if (uri.getScheme().startsWith("http")) { + configSources.addAll(tryHttpResource(uri, ordinal)); + } else { + throw ConfigMessages.msg.schemeNotSupported(uri.getScheme()); + } + } + return configSources; + } + } } diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/SystemOnlySourcesConfigBuilder.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/SystemOnlySourcesConfigBuilder.java index 47e088f99bf17..3c865a9586d3b 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/SystemOnlySourcesConfigBuilder.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/SystemOnlySourcesConfigBuilder.java @@ -5,7 +5,9 @@ public class SystemOnlySourcesConfigBuilder implements ConfigBuilder { @Override public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) { - return builder.setAddDefaultSources(false).addSystemSources(); + return builder.setAddDefaultSources(false) + .setAddPropertiesSources(false) + .addSystemSources(); } @Override diff --git a/extensions/config-yaml/deployment/src/main/java/io/quarkus/config/yaml/deployment/ConfigYamlProcessor.java b/extensions/config-yaml/deployment/src/main/java/io/quarkus/config/yaml/deployment/ConfigYamlProcessor.java index c25fab1f0f91a..98b9489c6b2a4 100644 --- a/extensions/config-yaml/deployment/src/main/java/io/quarkus/config/yaml/deployment/ConfigYamlProcessor.java +++ b/extensions/config-yaml/deployment/src/main/java/io/quarkus/config/yaml/deployment/ConfigYamlProcessor.java @@ -6,7 +6,8 @@ import org.eclipse.microprofile.config.ConfigProvider; -import io.quarkus.config.yaml.runtime.YamlConfigBuilder; +import io.quarkus.config.yaml.runtime.YamlInClassPathConfigBuilder; +import io.quarkus.config.yaml.runtime.YamlInFileSystemConfigBuilder; import io.quarkus.deployment.Feature; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; @@ -14,6 +15,7 @@ import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem; import io.quarkus.deployment.builditem.RunTimeConfigBuilderBuildItem; import io.quarkus.deployment.builditem.StaticInitConfigBuilderBuildItem; +import io.quarkus.deployment.pkg.NativeConfig; import io.smallrye.config.SmallRyeConfig; public final class ConfigYamlProcessor { @@ -24,12 +26,18 @@ public FeatureBuildItem feature() { } @BuildStep - public void yamlConfig( + void yamlConfig( + NativeConfig nativeConfig, BuildProducer staticInitConfigBuilder, BuildProducer runTimeConfigBuilder) { - staticInitConfigBuilder.produce(new StaticInitConfigBuilderBuildItem(YamlConfigBuilder.class)); - runTimeConfigBuilder.produce(new RunTimeConfigBuilderBuildItem(YamlConfigBuilder.class)); + staticInitConfigBuilder.produce(new StaticInitConfigBuilderBuildItem(YamlInFileSystemConfigBuilder.class)); + runTimeConfigBuilder.produce(new RunTimeConfigBuilderBuildItem(YamlInFileSystemConfigBuilder.class)); + + if (!nativeConfig.enabled()) { + staticInitConfigBuilder.produce(new StaticInitConfigBuilderBuildItem(YamlInClassPathConfigBuilder.class)); + runTimeConfigBuilder.produce(new RunTimeConfigBuilderBuildItem(YamlInClassPathConfigBuilder.class)); + } } @BuildStep diff --git a/extensions/config-yaml/runtime/src/main/java/io/quarkus/config/yaml/runtime/YamlConfigBuilder.java b/extensions/config-yaml/runtime/src/main/java/io/quarkus/config/yaml/runtime/YamlInClassPathConfigBuilder.java similarity index 58% rename from extensions/config-yaml/runtime/src/main/java/io/quarkus/config/yaml/runtime/YamlConfigBuilder.java rename to extensions/config-yaml/runtime/src/main/java/io/quarkus/config/yaml/runtime/YamlInClassPathConfigBuilder.java index 0d2045b995f14..1b977b1509fc7 100644 --- a/extensions/config-yaml/runtime/src/main/java/io/quarkus/config/yaml/runtime/YamlConfigBuilder.java +++ b/extensions/config-yaml/runtime/src/main/java/io/quarkus/config/yaml/runtime/YamlInClassPathConfigBuilder.java @@ -4,10 +4,19 @@ import io.smallrye.config.SmallRyeConfigBuilder; import io.smallrye.config.source.yaml.YamlConfigSourceLoader; -public class YamlConfigBuilder implements ConfigBuilder { +/** + * Only for JVM mode. + * + * @see io.quarkus.runtime.configuration.RuntimeConfigBuilder + */ +public class YamlInClassPathConfigBuilder implements ConfigBuilder { @Override public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) { - return builder.withSources(new YamlConfigSourceLoader.InFileSystem()) - .withSources(new YamlConfigSourceLoader.InClassPath()); + return builder.withSources(new YamlConfigSourceLoader.InClassPath()); + } + + @Override + public int priority() { + return Integer.MIN_VALUE + 100; } } diff --git a/extensions/config-yaml/runtime/src/main/java/io/quarkus/config/yaml/runtime/YamlInFileSystemConfigBuilder.java b/extensions/config-yaml/runtime/src/main/java/io/quarkus/config/yaml/runtime/YamlInFileSystemConfigBuilder.java new file mode 100644 index 0000000000000..543cbe4085505 --- /dev/null +++ b/extensions/config-yaml/runtime/src/main/java/io/quarkus/config/yaml/runtime/YamlInFileSystemConfigBuilder.java @@ -0,0 +1,25 @@ +package io.quarkus.config.yaml.runtime; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.microprofile.config.spi.ConfigSourceProvider; + +import io.quarkus.runtime.configuration.ConfigBuilder; +import io.smallrye.config.SmallRyeConfigBuilder; +import io.smallrye.config.source.yaml.YamlConfigSourceLoader; + +public class YamlInFileSystemConfigBuilder implements ConfigBuilder { + @Override + public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) { + // Removes Yaml source providers added by the ServiceLoader to avoid double registration + List toRemove = new ArrayList<>(); + for (ConfigSourceProvider sourceProvider : builder.getSourceProviders()) { + if (sourceProvider instanceof YamlConfigSourceLoader) { + toRemove.add(sourceProvider); + } + } + builder.getSourceProviders().removeAll(toRemove); + return builder.withSources(new YamlConfigSourceLoader.InFileSystem()); + } +}