From 902e8e953aea6aa4ecfacfbd36dceb031e96820d Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Tue, 11 Oct 2022 22:03:16 +0200 Subject: [PATCH] Enable more config providers for code generators except those found in the root app module --- .../io/quarkus/deployment/CodeGenerator.java | 81 ++++++++++++++----- .../deployment/dev/CodeGenWatcher.java | 5 +- .../gradle/tasks/QuarkusGenerateCode.java | 18 +++-- .../io/quarkus/maven/GenerateCodeMojo.java | 2 +- .../classloading/QuarkusClassLoader.java | 9 +++ 5 files changed, 86 insertions(+), 29 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/CodeGenerator.java b/core/deployment/src/main/java/io/quarkus/deployment/CodeGenerator.java index ef90eeee63e2b..b93c2a21ab98f 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/CodeGenerator.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/CodeGenerator.java @@ -7,10 +7,16 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.ServiceLoader; import java.util.function.Consumer; +import org.eclipse.microprofile.config.Config; + +import io.quarkus.bootstrap.classloading.ClassPathElement; +import io.quarkus.bootstrap.classloading.FilteredClassPathElement; +import io.quarkus.bootstrap.classloading.QuarkusClassLoader; import io.quarkus.bootstrap.model.ApplicationModel; import io.quarkus.bootstrap.prebuild.CodeGenException; import io.quarkus.deployment.codegen.CodeGenData; @@ -19,8 +25,11 @@ import io.quarkus.paths.PathCollection; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.configuration.ConfigUtils; +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; import io.smallrye.config.SysPropConfigSource; /** @@ -28,16 +37,23 @@ */ public class CodeGenerator { + private static final String MP_CONFIG_SPI_CONFIG_SOURCE_PROVIDER = "META-INF/services/org.eclipse.microprofile.config.spi.ConfigSourceProvider"; + // used by Gradle and Maven - public static void initAndRun(ClassLoader classLoader, + public static void initAndRun(QuarkusClassLoader classLoader, PathCollection sourceParentDirs, Path generatedSourcesDir, Path buildDir, Consumer sourceRegistrar, ApplicationModel appModel, Properties properties, String launchMode, boolean test) throws CodeGenException { final List generators = init(classLoader, sourceParentDirs, generatedSourcesDir, buildDir, sourceRegistrar); + if (generators.isEmpty()) { + return; + } + final LaunchMode mode = LaunchMode.valueOf(launchMode); + final Config config = getConfig(appModel, mode, properties, classLoader); for (CodeGenData generator : generators) { generator.setRedirectIO(true); - trigger(classLoader, generator, appModel, properties, LaunchMode.valueOf(launchMode), test); + trigger(classLoader, generator, appModel, config, test); } } @@ -51,7 +67,7 @@ private static List init(ClassLoader deploymentClassLoader, if (codeGenProviders.isEmpty()) { return List.of(); } - List result = new ArrayList<>(); + final List result = new ArrayList<>(codeGenProviders.size()); for (CodeGenProvider provider : codeGenProviders) { Path outputDir = codeGenOutDir(generatedSourcesDir, provider, sourceRegistrar); for (Path sourceParentDir : sourceParentDirs) { @@ -144,23 +160,9 @@ private static T callWithClassloader(ClassLoader deploymentClassLoader, Code public static boolean trigger(ClassLoader deploymentClassLoader, CodeGenData data, ApplicationModel appModel, - Properties properties, - LaunchMode launchMode, + Config config, boolean test) throws CodeGenException { return callWithClassloader(deploymentClassLoader, () -> { - - final PropertiesConfigSource pcs = new PropertiesConfigSource(properties, "Build system"); - final SysPropConfigSource spcs = new SysPropConfigSource(); - - // Discovered Config classes may cause issues here, because this goal runs before compile - final SmallRyeConfig config = ConfigUtils.configBuilder(false, false, launchMode) - .setAddDiscoveredSources(false) - .setAddDiscoveredInterceptors(false) - .setAddDiscoveredConverters(false) - .withProfile(launchMode.getDefaultProfile()) - .withSources(pcs, spcs) - .build(); - CodeGenProvider provider = data.provider; return provider.shouldRun(data.sourceDir, config) && provider.trigger( @@ -169,6 +171,47 @@ public static boolean trigger(ClassLoader deploymentClassLoader, }); } + public static Config getConfig(ApplicationModel appModel, LaunchMode launchMode, Properties buildSystemProps, + QuarkusClassLoader deploymentClassLoader) throws CodeGenException { + // Config instance that is returned by this method should be as close to the one built in the ExtensionLoader as possible + if (appModel.getAppArtifact().getContentTree() + .contains(MP_CONFIG_SPI_CONFIG_SOURCE_PROVIDER)) { + final List allElements = ((QuarkusClassLoader) deploymentClassLoader).getAllElements(false); + // we don't want to load config sources from the current module because they haven't been compiled yet + final QuarkusClassLoader.Builder configClBuilder = QuarkusClassLoader + .builder("CodeGenerator Config ClassLoader", QuarkusClassLoader.getSystemClassLoader(), false); + final Collection appRoots = appModel.getAppArtifact().getContentTree().getRoots(); + for (ClassPathElement e : allElements) { + if (appRoots.contains(e.getRoot())) { + configClBuilder.addElement(new FilteredClassPathElement(e, List.of(MP_CONFIG_SPI_CONFIG_SOURCE_PROVIDER))); + } else { + configClBuilder.addElement(e); + } + } + deploymentClassLoader = configClBuilder.build(); + } + final SmallRyeConfigBuilder builder = ConfigUtils.configBuilder(false, launchMode) + .forClassLoader(deploymentClassLoader); + final PropertiesConfigSource pcs = new PropertiesConfigSource(buildSystemProps, "Build system"); + final SysPropConfigSource spcs = new SysPropConfigSource(); + + final Map platformProperties = appModel.getPlatformProperties(); + if (platformProperties.isEmpty()) { + builder.withSources(pcs, spcs); + } else { + final KeyMap props = new KeyMap<>(platformProperties.size()); + for (Map.Entry 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(); + } + private static Path codeGenOutDir(Path generatedSourcesDir, CodeGenProvider provider, Consumer sourceRegistrar) throws CodeGenException { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/CodeGenWatcher.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/CodeGenWatcher.java index f20800cfd7a28..35c731d896f58 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/CodeGenWatcher.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/CodeGenWatcher.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Properties; +import org.eclipse.microprofile.config.Config; import org.jboss.logging.Logger; import io.quarkus.bootstrap.app.CuratedApplication; @@ -33,13 +34,15 @@ class CodeGenWatcher { final Collection watchers = new ArrayList<>(codeGens.size()); final Properties properties = new Properties(); properties.putAll(context.getBuildSystemProperties()); + final Config config = CodeGenerator.getConfig(curatedApplication.getApplicationModel(), LaunchMode.DEVELOPMENT, + properties, deploymentClassLoader); for (CodeGenData codeGen : codeGens) { watchers.add(new FSWatchUtil.Watcher(codeGen.sourceDir, codeGen.provider.inputExtension(), modifiedPaths -> { try { CodeGenerator.trigger(deploymentClassLoader, codeGen, - curatedApplication.getApplicationModel(), properties, LaunchMode.DEVELOPMENT, false); + curatedApplication.getApplicationModel(), config, false); } catch (Exception any) { log.warn("Code generation failed", any); } diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java index e0c1ed989c64e..c10b0b814a4e6 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java @@ -6,10 +6,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.List; -import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.function.Consumer; @@ -29,6 +27,7 @@ import io.quarkus.bootstrap.classloading.QuarkusClassLoader; import io.quarkus.bootstrap.model.ApplicationModel; import io.quarkus.deployment.CodeGenerator; +import io.quarkus.paths.PathCollection; import io.quarkus.paths.PathList; import io.quarkus.runtime.LaunchMode; @@ -125,14 +124,17 @@ public void prepareQuarkus() { QuarkusClassLoader deploymentClassLoader = appCreationContext.createDeploymentClassLoader(); Class codeGenerator = deploymentClassLoader.loadClass(CodeGenerator.class.getName()); - Optional initAndRun = Arrays.stream(codeGenerator.getMethods()) - .filter(m -> m.getName().equals(INIT_AND_RUN)) - .findAny(); - if (initAndRun.isEmpty()) { - throw new GradleException("Failed to find " + INIT_AND_RUN + " method in " + CodeGenerator.class.getName()); + Method initAndRun; + try { + initAndRun = codeGenerator.getMethod(INIT_AND_RUN, QuarkusClassLoader.class, PathCollection.class, + Path.class, Path.class, + Consumer.class, ApplicationModel.class, Properties.class, String.class, + boolean.class); + } catch (Exception e) { + throw new GradleException("Quarkus code generation phase has failed", e); } - initAndRun.get().invoke(null, deploymentClassLoader, + initAndRun.invoke(null, deploymentClassLoader, PathList.from(sourcesDirectories), paths.get(0), buildDir, diff --git a/devtools/maven/src/main/java/io/quarkus/maven/GenerateCodeMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/GenerateCodeMojo.java index ac534f4f9756c..7d34a481f2dbc 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/GenerateCodeMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/GenerateCodeMojo.java @@ -72,7 +72,7 @@ void generateCode(PathCollection sourceParents, Thread.currentThread().setContextClassLoader(deploymentClassLoader); final Class codeGenerator = deploymentClassLoader.loadClass("io.quarkus.deployment.CodeGenerator"); - final Method initAndRun = codeGenerator.getMethod("initAndRun", ClassLoader.class, PathCollection.class, + final Method initAndRun = codeGenerator.getMethod("initAndRun", QuarkusClassLoader.class, PathCollection.class, Path.class, Path.class, Consumer.class, ApplicationModel.class, Properties.class, String.class, boolean.class); diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java index fc3a2366c740a..d269b949db945 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java @@ -48,6 +48,15 @@ public static List getElements(String resourceName, boolean lo return ((QuarkusClassLoader) ccl).getElementsWithResource(resourceName, localOnly); } + public List getAllElements(boolean localOnly) { + List ret = new ArrayList<>(); + if (parent instanceof QuarkusClassLoader && !localOnly) { + ret.addAll(((QuarkusClassLoader) parent).getAllElements(localOnly)); + } + ret.addAll(elements); + return ret; + } + /** * Indicates if a given class is present at runtime. *