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 9b39e7d44947c..87c70a6c30e1e 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/CodeGenerator.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/CodeGenerator.java @@ -4,6 +4,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.ServiceLoader; @@ -12,6 +14,8 @@ import io.quarkus.bootstrap.model.ApplicationModel; import io.quarkus.bootstrap.prebuild.CodeGenException; import io.quarkus.deployment.codegen.CodeGenData; +import io.quarkus.deployment.dev.DevModeContext; +import io.quarkus.deployment.dev.DevModeContext.ModuleInfo; import io.quarkus.paths.PathCollection; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.configuration.ConfigUtils; @@ -28,40 +32,92 @@ public static void initAndRun(ClassLoader classLoader, PathCollection sourceParentDirs, Path generatedSourcesDir, Path buildDir, Consumer sourceRegistrar, ApplicationModel appModel, Properties properties, String launchMode, boolean test) throws CodeGenException { - List generators = init(classLoader, sourceParentDirs, generatedSourcesDir, buildDir, sourceRegistrar); + final List generators = init(classLoader, sourceParentDirs, generatedSourcesDir, buildDir, + sourceRegistrar); for (CodeGenData generator : generators) { generator.setRedirectIO(true); trigger(classLoader, generator, appModel, properties, LaunchMode.valueOf(launchMode), test); } } - public static List init(ClassLoader deploymentClassLoader, + private static List init(ClassLoader deploymentClassLoader, PathCollection sourceParentDirs, Path generatedSourcesDir, Path buildDir, Consumer sourceRegistrar) throws CodeGenException { return callWithClassloader(deploymentClassLoader, () -> { - List result = new ArrayList<>(); - Class codeGenProviderClass; - try { - //noinspection unchecked - codeGenProviderClass = (Class) deploymentClassLoader - .loadClass(CodeGenProvider.class.getName()); - } catch (ClassNotFoundException e) { - throw new CodeGenException("Failed to load CodeGenProvider class from deployment classloader", e); + final List codeGenProviders = loadCodeGenProviders(deploymentClassLoader); + if (codeGenProviders.isEmpty()) { + return List.of(); } - for (CodeGenProvider provider : ServiceLoader.load(codeGenProviderClass, deploymentClassLoader)) { + List result = new ArrayList<>(); + for (CodeGenProvider provider : codeGenProviders) { Path outputDir = codeGenOutDir(generatedSourcesDir, provider, sourceRegistrar); for (Path sourceParentDir : sourceParentDirs) { result.add( new CodeGenData(provider, outputDir, sourceParentDir.resolve(provider.inputDirectory()), buildDir)); } } - return result; }); } + public static List init(ClassLoader deploymentClassLoader, Collection modules) + throws CodeGenException { + return callWithClassloader(deploymentClassLoader, () -> { + List codeGenProviders = null; + List codeGens = List.of(); + for (DevModeContext.ModuleInfo module : modules) { + if (!module.getSourceParents().isEmpty() && module.getPreBuildOutputDir() != null) { // it's null for remote dev + + if (codeGenProviders == null) { + codeGenProviders = loadCodeGenProviders(deploymentClassLoader); + if (codeGenProviders.isEmpty()) { + return List.of(); + } + } + + for (CodeGenProvider provider : codeGenProviders) { + Path outputDir = codeGenOutDir(Path.of(module.getPreBuildOutputDir()), provider, + sourcePath -> module.addSourcePathFirst(sourcePath.toAbsolutePath().toString())); + for (Path sourceParentDir : module.getSourceParents()) { + if (codeGens.isEmpty()) { + codeGens = new ArrayList<>(); + } + codeGens.add( + new CodeGenData(provider, outputDir, sourceParentDir.resolve(provider.inputDirectory()), + Path.of(module.getTargetDir()))); + } + + } + } + } + return codeGens; + }); + } + + private static List loadCodeGenProviders(ClassLoader deploymentClassLoader) + throws CodeGenException { + Class codeGenProviderClass; + try { + //noinspection unchecked + codeGenProviderClass = (Class) deploymentClassLoader + .loadClass(CodeGenProvider.class.getName()); + } catch (ClassNotFoundException e) { + throw new CodeGenException("Failed to load CodeGenProvider class from deployment classloader", e); + } + final Iterator i = ServiceLoader.load(codeGenProviderClass, deploymentClassLoader) + .iterator(); + if (!i.hasNext()) { + return List.of(); + } + final List codeGenProviders = new ArrayList<>(); + while (i.hasNext()) { + codeGenProviders.add(i.next()); + } + return codeGenProviders; + } + private static T callWithClassloader(ClassLoader deploymentClassLoader, CodeGenAction supplier) throws CodeGenException { ClassLoader originalClassloader = Thread.currentThread().getContextClassLoader(); 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 new file mode 100644 index 0000000000000..f20800cfd7a28 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/CodeGenWatcher.java @@ -0,0 +1,62 @@ +package io.quarkus.deployment.dev; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Properties; + +import org.jboss.logging.Logger; + +import io.quarkus.bootstrap.app.CuratedApplication; +import io.quarkus.bootstrap.classloading.QuarkusClassLoader; +import io.quarkus.bootstrap.prebuild.CodeGenException; +import io.quarkus.deployment.CodeGenerator; +import io.quarkus.deployment.codegen.CodeGenData; +import io.quarkus.deployment.util.FSWatchUtil; +import io.quarkus.runtime.LaunchMode; + +class CodeGenWatcher { + + private static final Logger log = Logger.getLogger(CodeGenWatcher.class); + + private final QuarkusClassLoader deploymentClassLoader; + private final FSWatchUtil fsWatchUtil; + + CodeGenWatcher(CuratedApplication curatedApplication, DevModeContext context) throws CodeGenException { + final QuarkusClassLoader deploymentClassLoader = curatedApplication.createDeploymentClassLoader(); + final List codeGens = CodeGenerator.init(deploymentClassLoader, context.getAllModules()); + if (codeGens.isEmpty()) { + fsWatchUtil = null; + this.deploymentClassLoader = null; + deploymentClassLoader.close(); + } else { + final Collection watchers = new ArrayList<>(codeGens.size()); + final Properties properties = new Properties(); + properties.putAll(context.getBuildSystemProperties()); + 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); + } catch (Exception any) { + log.warn("Code generation failed", any); + } + })); + } + fsWatchUtil = new FSWatchUtil(); + fsWatchUtil.observe(watchers, 500); + this.deploymentClassLoader = deploymentClassLoader; + } + } + + void shutdown() { + if (fsWatchUtil != null) { + fsWatchUtil.shutdown(); + } + if (deploymentClassLoader != null) { + deploymentClassLoader.close(); + } + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedDevModeMain.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedDevModeMain.java index fddec59681315..94683f410e3d2 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedDevModeMain.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedDevModeMain.java @@ -10,14 +10,10 @@ import java.io.ObjectOutputStream; import java.net.BindException; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.Properties; import java.util.ServiceLoader; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -45,15 +41,12 @@ import io.quarkus.builder.BuildChainBuilder; import io.quarkus.builder.BuildContext; import io.quarkus.builder.BuildStep; -import io.quarkus.deployment.CodeGenerator; import io.quarkus.deployment.builditem.ApplicationClassPredicateBuildItem; -import io.quarkus.deployment.codegen.CodeGenData; import io.quarkus.deployment.console.ConsoleCommand; import io.quarkus.deployment.console.ConsoleStateManager; import io.quarkus.deployment.dev.testing.MessageFormat; import io.quarkus.deployment.dev.testing.TestSupport; import io.quarkus.deployment.steps.ClassTransformingBuildStep; -import io.quarkus.deployment.util.FSWatchUtil; import io.quarkus.dev.appstate.ApplicationStartException; import io.quarkus.dev.console.DevConsoleManager; import io.quarkus.dev.spi.DeploymentFailedStartHandler; @@ -61,7 +54,6 @@ import io.quarkus.dev.spi.HotReplacementSetup; import io.quarkus.runner.bootstrap.AugmentActionImpl; import io.quarkus.runtime.ApplicationLifecycleManager; -import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.configuration.QuarkusConfigFactory; import io.quarkus.runtime.logging.LoggingSetupRecorder; @@ -81,11 +73,11 @@ public class IsolatedDevModeMain implements BiConsumer listeners = new ArrayList<>(); - private synchronized void firstStart(QuarkusClassLoader deploymentClassLoader, List codeGens) { + private synchronized void firstStart() { ClassLoader old = Thread.currentThread().getContextClassLoader(); try { @@ -143,7 +135,6 @@ public void accept(String args) { } }); - startCodeGenWatcher(deploymentClassLoader, codeGens, context.getBuildSystemProperties()); runner = start.runMainClass(context.getArgs()); RuntimeUpdatesProcessor.INSTANCE.setConfiguredInstrumentationEnabled( runner.getConfigValue("quarkus.live-reload.instrumentation", Boolean.class).orElse(false)); @@ -209,27 +200,6 @@ public void accept(String args) { } } - private void startCodeGenWatcher(QuarkusClassLoader classLoader, List codeGens, - Map propertyMap) { - - Collection watchers = new ArrayList<>(); - Properties properties = new Properties(); - properties.putAll(propertyMap); - for (CodeGenData codeGen : codeGens) { - watchers.add(new FSWatchUtil.Watcher(codeGen.sourceDir, codeGen.provider.inputExtension(), - modifiedPaths -> { - try { - CodeGenerator.trigger(classLoader, - codeGen, - curatedApplication.getApplicationModel(), properties, LaunchMode.DEVELOPMENT, false); - } catch (Exception any) { - log.warn("Code generation failed", any); - } - })); - } - fsWatchUtil.observe(watchers, 500); - } - public void restartCallback(Set changedResources, ClassScanResult result) { restartApp(changedResources, new ClassChangeInformation(result.changedClassNames, result.deletedClassNames, result.addedClassNames)); @@ -351,7 +321,9 @@ public void stop() { public void close() { //don't attempt to restart in the exit code handler restarting = true; - fsWatchUtil.shutdown(); + if (codeGenWatcher != null) { + codeGenWatcher.shutdown(); + } for (int i = listeners.size() - 1; i >= 0; i--) { try { @@ -439,7 +411,7 @@ public void run() { } augmentAction = new AugmentActionImpl(curatedApplication, - Collections.singletonList(new Consumer() { + List.of(new Consumer() { @Override public void accept(BuildChainBuilder buildChainBuilder) { buildChainBuilder.addBuildStep(new BuildStep() { @@ -461,28 +433,19 @@ public boolean test(String s) { }).produces(ApplicationClassPredicateBuildItem.class).build(); } }), - Collections.emptyList()); - List codeGens = new ArrayList<>(); - QuarkusClassLoader deploymentClassLoader = curatedApplication.createDeploymentClassLoader(); - - for (DevModeContext.ModuleInfo module : context.getAllModules()) { - if (!module.getSourceParents().isEmpty() && module.getPreBuildOutputDir() != null) { // it's null for remote dev - codeGens.addAll( - CodeGenerator.init( - deploymentClassLoader, - module.getSourceParents(), - Paths.get(module.getPreBuildOutputDir()), - Paths.get(module.getTargetDir()), - sourcePath -> module.addSourcePathFirst(sourcePath.toAbsolutePath().toString()))); - } - } + List.of()); + + // code generators should be initialized before the runtime compilation is setup to properly configure the sources directories + codeGenWatcher = new CodeGenWatcher(curatedApplication, context); + RuntimeUpdatesProcessor.INSTANCE = setupRuntimeCompilation(context, (Path) params.get(APP_ROOT), (DevModeType) params.get(DevModeType.class.getName())); if (RuntimeUpdatesProcessor.INSTANCE != null) { RuntimeUpdatesProcessor.INSTANCE.checkForFileChange(); RuntimeUpdatesProcessor.INSTANCE.checkForChangedClasses(true); } - firstStart(deploymentClassLoader, codeGens); + + firstStart(); // doStart(false, Collections.emptySet()); if (deploymentProblem != null || RuntimeUpdatesProcessor.INSTANCE.getCompileProblem() != null) { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/JavaCompilationProvider.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/JavaCompilationProvider.java index 477638127eb1f..87ff416e756fa 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/JavaCompilationProvider.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/JavaCompilationProvider.java @@ -6,9 +6,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; +import java.util.List; import java.util.Set; import javax.tools.Diagnostic; @@ -33,8 +31,8 @@ public class JavaCompilationProvider implements CompilationProvider { // -g is used to make the java compiler generate all debugging info // -parameters is used to generate metadata for reflection on method parameters // this is useful when people using debuggers against their hot-reloaded app - private static final Set COMPILER_OPTIONS = new HashSet<>(Arrays.asList("-g", "-parameters")); - private static final Set IGNORE_NAMESPACES = new HashSet<>(Collections.singletonList("org.osgi")); + private static final Set COMPILER_OPTIONS = Set.of("-g", "-parameters"); + private static final Set IGNORE_NAMESPACES = Set.of("org.osgi"); JavaCompiler compiler; StandardJavaFileManager fileManager; @@ -47,7 +45,7 @@ public String getProviderKey() { @Override public Set handledExtensions() { - return Collections.singleton(".java"); + return Set.of(".java"); } @Override @@ -67,7 +65,7 @@ public void compile(Set filesToCompile, Context context) { DiagnosticCollector diagnostics = new DiagnosticCollector<>(); fileManager.setLocation(StandardLocation.CLASS_PATH, context.getClasspath()); - fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(context.getOutputDirectory())); + fileManager.setLocation(StandardLocation.CLASS_OUTPUT, List.of(context.getOutputDirectory())); CompilerFlags compilerFlags = new CompilerFlags(COMPILER_OPTIONS, context.getCompilerOptions(getProviderKey()), context.getReleaseJavaVersion(), context.getSourceJavaVersion(), context.getTargetJvmVersion()); diff --git a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java index 7211f144c3f9e..cf66ecbbcd052 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java @@ -101,7 +101,6 @@ import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.maven.dependency.GACT; -import io.quarkus.maven.dependency.GACTV; import io.quarkus.maven.dependency.ResolvedDependency; import io.quarkus.paths.PathList; import io.quarkus.runtime.LaunchMode; @@ -1056,7 +1055,7 @@ private QuarkusDevModeLauncher newLauncher() throws Exception { appModel = new BootstrapAppModelResolver(resolverBuilder.build()) .setDevMode(true) .setCollectReloadableDependencies(!noDeps) - .resolveModel(GACTV.jar(project.getGroupId(), project.getArtifactId(), project.getVersion())); + .resolveModel(ArtifactCoords.jar(project.getGroupId(), project.getArtifactId(), project.getVersion())); } // serialize the app model to avoid re-resolving it in the dev process 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 20b82611c5190..ac534f4f9756c 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/GenerateCodeMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/GenerateCodeMojo.java @@ -65,11 +65,10 @@ void generateCode(PathCollection sourceParents, ClassLoader originalTccl = Thread.currentThread().getContextClassLoader(); CuratedApplication curatedApplication = null; + QuarkusClassLoader deploymentClassLoader = null; try { - curatedApplication = bootstrapApplication(launchMode); - - QuarkusClassLoader deploymentClassLoader = curatedApplication.createDeploymentClassLoader(); + deploymentClassLoader = curatedApplication.createDeploymentClassLoader(); Thread.currentThread().setContextClassLoader(deploymentClassLoader); final Class codeGenerator = deploymentClassLoader.loadClass("io.quarkus.deployment.CodeGenerator"); @@ -90,6 +89,9 @@ void generateCode(PathCollection sourceParents, curatedApplication.close(); } Thread.currentThread().setContextClassLoader(originalTccl); + if (deploymentClassLoader != null) { + deploymentClassLoader.close(); + } } } diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/util/IoUtils.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/util/IoUtils.java index bcb980818e07f..ba28f563ad583 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/util/IoUtils.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/util/IoUtils.java @@ -120,33 +120,34 @@ public static void createOrEmptyDir(Path dir) throws IOException { public static Path copy(Path source, Path target) throws IOException { if (Files.isDirectory(source)) { Files.createDirectories(target); - } else { - Files.createDirectories(target.getParent()); - } - Files.walkFileTree(source, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, - new SimpleFileVisitor() { - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) - throws IOException { - final Path targetDir = target.resolve(source.relativize(dir).toString()); - try { - Files.copy(dir, targetDir); - } catch (FileAlreadyExistsException e) { - if (!Files.isDirectory(targetDir)) { - throw e; + Files.walkFileTree(source, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, + new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) + throws IOException { + final Path targetDir = target.resolve(source.relativize(dir).toString()); + try { + Files.copy(dir, targetDir); + } catch (FileAlreadyExistsException e) { + if (!Files.isDirectory(targetDir)) { + throw e; + } } + return FileVisitResult.CONTINUE; } - return FileVisitResult.CONTINUE; - } - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) - throws IOException { - Files.copy(file, target.resolve(source.relativize(file).toString()), - StandardCopyOption.REPLACE_EXISTING); - return FileVisitResult.CONTINUE; - } - }); + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + Files.copy(file, target.resolve(source.relativize(file).toString()), + StandardCopyOption.REPLACE_EXISTING); + return FileVisitResult.CONTINUE; + } + }); + } else { + Files.createDirectories(target.getParent()); + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); + } return target; } diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java index 8236688afac1b..eedf5a1a1e0f3 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java @@ -409,6 +409,7 @@ public void close() { if (baseRuntimeClassLoader != null) { baseRuntimeClassLoader.close(); } + augmentationElements.clear(); } /**