Skip to content

Commit

Permalink
Load CodeGenProvider services once instead of once per module
Browse files Browse the repository at this point in the history
  • Loading branch information
aloubyansky committed Aug 15, 2022
1 parent bccd9a1 commit 6b7f1ff
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 98 deletions.
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 = null;
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 == null) {
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,18 @@ 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());

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();

codeGenWatcher = new CodeGenWatcher(curatedApplication, context);

// 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

0 comments on commit 6b7f1ff

Please sign in to comment.