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

Load CodeGenProvider services once instead of once per module #27288

Merged
merged 1 commit into from Aug 16, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -28,40 +32,92 @@ public static void initAndRun(ClassLoader classLoader,
PathCollection sourceParentDirs, Path generatedSourcesDir, Path buildDir,
Consumer<Path> sourceRegistrar, ApplicationModel appModel, Properties properties,
String launchMode, boolean test) throws CodeGenException {
List<CodeGenData> generators = init(classLoader, sourceParentDirs, generatedSourcesDir, buildDir, sourceRegistrar);
final List<CodeGenData> 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<CodeGenData> init(ClassLoader deploymentClassLoader,
private static List<CodeGenData> init(ClassLoader deploymentClassLoader,
PathCollection sourceParentDirs,
Path generatedSourcesDir,
Path buildDir,
Consumer<Path> sourceRegistrar) throws CodeGenException {
return callWithClassloader(deploymentClassLoader, () -> {
List<CodeGenData> result = new ArrayList<>();
Class<? extends CodeGenProvider> codeGenProviderClass;
try {
//noinspection unchecked
codeGenProviderClass = (Class<? extends CodeGenProvider>) deploymentClassLoader
.loadClass(CodeGenProvider.class.getName());
} catch (ClassNotFoundException e) {
throw new CodeGenException("Failed to load CodeGenProvider class from deployment classloader", e);
final List<CodeGenProvider> codeGenProviders = loadCodeGenProviders(deploymentClassLoader);
if (codeGenProviders.isEmpty()) {
return List.of();
}
for (CodeGenProvider provider : ServiceLoader.load(codeGenProviderClass, deploymentClassLoader)) {
List<CodeGenData> 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<CodeGenData> init(ClassLoader deploymentClassLoader, Collection<ModuleInfo> modules)
throws CodeGenException {
return callWithClassloader(deploymentClassLoader, () -> {
List<CodeGenProvider> codeGenProviders = null;
List<CodeGenData> 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<CodeGenProvider> loadCodeGenProviders(ClassLoader deploymentClassLoader)
throws CodeGenException {
Class<? extends CodeGenProvider> codeGenProviderClass;
try {
//noinspection unchecked
codeGenProviderClass = (Class<? extends CodeGenProvider>) deploymentClassLoader
.loadClass(CodeGenProvider.class.getName());
} catch (ClassNotFoundException e) {
throw new CodeGenException("Failed to load CodeGenProvider class from deployment classloader", e);
}
final Iterator<? extends CodeGenProvider> i = ServiceLoader.load(codeGenProviderClass, deploymentClassLoader)
.iterator();
if (!i.hasNext()) {
return List.of();
}
final List<CodeGenProvider> codeGenProviders = new ArrayList<>();
while (i.hasNext()) {
codeGenProviders.add(i.next());
}
return codeGenProviders;
}

private static <T> T callWithClassloader(ClassLoader deploymentClassLoader, CodeGenAction<T> supplier)
throws CodeGenException {
ClassLoader originalClassloader = Thread.currentThread().getContextClassLoader();
Expand Down
@@ -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<CodeGenData> codeGens = CodeGenerator.init(deploymentClassLoader, context.getAllModules());
if (codeGens.isEmpty()) {
fsWatchUtil = null;
this.deploymentClassLoader = null;
deploymentClassLoader.close();
} else {
final Collection<FSWatchUtil.Watcher> 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();
}
}
}
Expand Up @@ -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;
Expand Down Expand Up @@ -45,23 +41,19 @@
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;
import io.quarkus.dev.spi.DevModeType;
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;

Expand All @@ -81,11 +73,11 @@ public class IsolatedDevModeMain implements BiConsumer<CuratedApplication, Map<S
private static volatile boolean firstStartCompleted;
private static final CountDownLatch shutdownLatch = new CountDownLatch(1);
private Thread shutdownThread;
private final FSWatchUtil fsWatchUtil = new FSWatchUtil();
private CodeGenWatcher codeGenWatcher;
private static volatile ConsoleStateManager.ConsoleContext consoleContext;
private final List<DevModeListener> listeners = new ArrayList<>();

private synchronized void firstStart(QuarkusClassLoader deploymentClassLoader, List<CodeGenData> codeGens) {
private synchronized void firstStart() {

ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -209,27 +200,6 @@ public void accept(String args) {
}
}

private void startCodeGenWatcher(QuarkusClassLoader classLoader, List<CodeGenData> codeGens,
Map<String, String> propertyMap) {

Collection<FSWatchUtil.Watcher> 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<String> changedResources, ClassScanResult result) {
restartApp(changedResources,
new ClassChangeInformation(result.changedClassNames, result.deletedClassNames, result.addedClassNames));
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -439,7 +411,7 @@ public void run() {
}

augmentAction = new AugmentActionImpl(curatedApplication,
Collections.singletonList(new Consumer<BuildChainBuilder>() {
List.of(new Consumer<BuildChainBuilder>() {
@Override
public void accept(BuildChainBuilder buildChainBuilder) {
buildChainBuilder.addBuildStep(new BuildStep() {
Expand All @@ -461,28 +433,19 @@ public boolean test(String s) {
}).produces(ApplicationClassPredicateBuildItem.class).build();
}
}),
Collections.emptyList());
List<CodeGenData> 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) {
Expand Down
Expand Up @@ -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;
Expand All @@ -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<String> COMPILER_OPTIONS = new HashSet<>(Arrays.asList("-g", "-parameters"));
private static final Set<String> IGNORE_NAMESPACES = new HashSet<>(Collections.singletonList("org.osgi"));
private static final Set<String> COMPILER_OPTIONS = Set.of("-g", "-parameters");
private static final Set<String> IGNORE_NAMESPACES = Set.of("org.osgi");

JavaCompiler compiler;
StandardJavaFileManager fileManager;
Expand All @@ -47,7 +45,7 @@ public String getProviderKey() {

@Override
public Set<String> handledExtensions() {
return Collections.singleton(".java");
return Set.of(".java");
}

@Override
Expand All @@ -67,7 +65,7 @@ public void compile(Set<File> filesToCompile, Context context) {

DiagnosticCollector<JavaFileObject> 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());
Expand Down
3 changes: 1 addition & 2 deletions devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down