Skip to content

Commit

Permalink
perf: Improve performance of Quarkus deployment jar support
Browse files Browse the repository at this point in the history
Fixes #1143

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Oct 10, 2023
1 parent 7d107b0 commit 9c72598
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public static boolean isQuarkusExtensionWithDeploymentArtifact(Library library)
if (library != null) {
VirtualFile[] files = library.getFiles(OrderRootType.CLASSES);

for(int i=0; !result && i < files.length;++i) {
for (int i = 0; !result && i < files.length; ++i) {
if (files[i].isDirectory()) {
result = ToolDelegate.getDeploymentJarId(VfsUtilCore.virtualToIoFile(files[i])) != null;
}
Expand All @@ -77,7 +77,7 @@ public static boolean isQuarkusExtensionWithDeploymentArtifact(Library library)
/**
* Check if the Quarkus library needs to be recomputed and update it if required.
*
* @param module the module to check
* @param module the module to check
* @param progressIndicator
*/
public static void ensureQuarkusLibrary(Module module, ProgressIndicator progressIndicator) {
Expand All @@ -95,20 +95,28 @@ public static void ensureQuarkusLibrary(Module module, ProgressIndicator progres
Integer actualHash = computeHash(module);
var qlib = OrderEntryUtil.findLibraryOrderEntry(ModuleRootManager.getInstance(module), QuarkusConstants.QUARKUS_DEPLOYMENT_LIBRARY_NAME);
if (qlib == null || (actualHash != null && !actualHash.equals(previousHash)) ||
!QuarkusConstants.QUARKUS_DEPLOYMENT_LIBRARY_VERSION.equals(component.getVersion())){
!QuarkusConstants.QUARKUS_DEPLOYMENT_LIBRARY_VERSION.equals(component.getVersion())) {
ModuleRootModificationUtil.updateModel(module, model -> {
LibraryTable table = model.getModuleLibraryTable();
Library library = table.getLibraryByName(QuarkusConstants.QUARKUS_DEPLOYMENT_LIBRARY_NAME);
while (library != null) {
table.removeLibrary(library);
TelemetryService.instance().action(TelemetryService.MODEL_PREFIX + "removeLibrary");
try {
TelemetryService.instance().action(TelemetryService.MODEL_PREFIX + "removeLibrary");
} catch (Exception e) {

}
library = table.getLibraryByName(QuarkusConstants.QUARKUS_DEPLOYMENT_LIBRARY_NAME);
}
progressIndicator.checkCanceled();
progressIndicator.setText("Collecting Quarkus deployment dependencies...");
List<VirtualFile>[] files = toolDelegate.getDeploymentFiles(module, progressIndicator);
LOGGER.info("Adding library to " + module.getName() + " previousHash=" + previousHash + " newHash=" + actualHash);
TelemetryService.instance().action(TelemetryService.MODEL_PREFIX + "addLibrary").send();
try {
TelemetryService.instance().action(TelemetryService.MODEL_PREFIX + "addLibrary").send();
} catch (Exception e) {

}
addLibrary(model, files, module);
});
component.setHash(actualHash);
Expand All @@ -120,7 +128,7 @@ public static void ensureQuarkusLibrary(Module module, ProgressIndicator progres
}

private static void addLibrary(ModifiableRootModel model, List<VirtualFile>[] files, Module module) {
LibraryEx library = (LibraryEx)model.getModuleLibraryTable().createLibrary(QuarkusConstants.QUARKUS_DEPLOYMENT_LIBRARY_NAME);
LibraryEx library = (LibraryEx) model.getModuleLibraryTable().createLibrary(QuarkusConstants.QUARKUS_DEPLOYMENT_LIBRARY_NAME);
LibraryEx.ModifiableModelEx libraryModel = library.getModifiableModel();

for (VirtualFile rootFile : files[ToolDelegate.BINARY]) {
Expand All @@ -145,14 +153,14 @@ private static Integer computeHash(Module module) {
@Override
public Set<String> visitLibraryOrderEntry(@NotNull LibraryOrderEntry libraryOrderEntry, Set<String> value) {
if (!isQuarkusDeploymentLibrary(libraryOrderEntry) && isQuarkusExtensionWithDeploymentArtifact(libraryOrderEntry.getLibrary())) {
for(VirtualFile file : libraryOrderEntry.getFiles(OrderRootType.CLASSES)) {
for (VirtualFile file : libraryOrderEntry.getFiles(OrderRootType.CLASSES)) {
value.add(file.getPath());
}
}
return value;
}
}, new HashSet<>());
return files.isEmpty()?null:files.hashCode();
return files.isEmpty() ? null : files.hashCode();
}

/**
Expand All @@ -174,7 +182,7 @@ public Boolean visitLibraryOrderEntry(@NotNull LibraryOrderEntry libraryOrderEnt
}

public static boolean isQuarkusLibrary(@NotNull LibraryOrderEntry libraryOrderEntry) {
return libraryOrderEntry.getLibraryName() != null &&
return libraryOrderEntry.getLibraryName() != null &&
libraryOrderEntry.getLibraryName().contains(QuarkusConstants.QUARKUS_CORE_PREFIX);
}

Expand Down Expand Up @@ -204,9 +212,9 @@ public static boolean checkQuarkusVersion(Module module, Predicate<Matcher> pred
.findFirst();
if (quarkusCoreJar.isPresent()) {
Matcher quarkusCoreArtifactMatcher = QUARKUS_CORE_PATTERN.matcher(quarkusCoreJar.get().getName());
if(quarkusCoreArtifactMatcher.matches()) {
if (quarkusCoreArtifactMatcher.matches()) {
String quarkusVersion = quarkusCoreArtifactMatcher.group(1);
LOGGER.debug("Detected Quarkus version = " + quarkusVersion);
LOGGER.debug("Detected Quarkus version = " + quarkusVersion);
Matcher quarkusVersionMatcher = QUARKUS_STANDARD_VERSIONING.matcher(quarkusVersion);
return predicate.test(quarkusVersionMatcher);
} else {
Expand All @@ -219,7 +227,7 @@ public static boolean checkQuarkusVersion(Module module, Predicate<Matcher> pred

public static Set<String> getModulesURIs(Project project) {
Set<String> uris = new HashSet<>();
for(Module module : ModuleManager.getInstance(project).getModules()) {
for (Module module : ModuleManager.getInstance(project).getModules()) {
uris.add(PsiUtilsLSImpl.getProjectURI(module));
}
return uris;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public QuarkusProjectService(Project project) {

@Override
public void moduleAdded(@NotNull Project project, @NotNull Module module) {
QuarkusModuleUtil.ensureQuarkusLibrary(module, new EmptyProgressIndicator());
//QuarkusModuleUtil.ensureQuarkusLibrary(module, new EmptyProgressIndicator());
}

public VirtualFile getSchema(Module module) {
Expand Down Expand Up @@ -141,7 +141,7 @@ public void processModules(com.intellij.openapi.progress.ProgressIndicator progr
if (!project.isDisposed()) {
for (var module : ModuleManager.getInstance(project).getModules()) {
LOGGER.info("Calling ensure from processModules");
QuarkusModuleUtil.ensureQuarkusLibrary(module, progressIndicator);
// QuarkusModuleUtil.ensureQuarkusLibrary(module, progressIndicator);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.redhat.devtools.intellij.quarkus.deployment;

import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.vfs.VirtualFile;
import com.redhat.devtools.intellij.lsp4ij.client.CoalesceByKey;
import com.redhat.devtools.intellij.lsp4ij.internal.PromiseToCompletableFuture;
import com.redhat.devtools.intellij.quarkus.tool.ToolDelegate;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

import static com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil.isQuarkusModule;

public class QuarkusDeploymentModuleLoader extends PromiseToCompletableFuture<List<VirtualFile>[]> {

private static final Logger LOGGER = LoggerFactory.getLogger(QuarkusDeploymentModuleLoader.class);

public QuarkusDeploymentModuleLoader(@NotNull Module module) {
super(progressIndicator -> {
return collectDeploymentFiles(module, progressIndicator);
}, "Quarkus deployment module loader", module.getProject(), module, new CoalesceByKey(QuarkusDeploymentModuleLoader.class.getName(), module.getName()));
init();
}

private static List<VirtualFile>[] collectDeploymentFiles(Module module, ProgressIndicator progressIndicator) {
if (module.isDisposed())
return null;
LOGGER.info("Ensuring library to " + module.getName());
long start = System.currentTimeMillis();
ToolDelegate toolDelegate = ToolDelegate.getDelegate(module);
if (toolDelegate == null) {
return null;
}
LOGGER.info("Tool delegate found for " + module.getName());
if (!isQuarkusModule(module)) {
return null;
}
LOGGER.info("isQuarkus module " + module.getName());
return toolDelegate.getDeploymentFiles(module, progressIndicator);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.redhat.devtools.intellij.quarkus.deployment;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.impl.libraries.LibraryEx;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.redhat.devtools.intellij.quarkus.QuarkusConstants;
import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil;
import com.redhat.devtools.intellij.quarkus.tool.ToolDelegate;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;

public class QuarkusDeploymentSupport {


private static final Key<CompletableFuture<Void>> QUARKUS_DEPLOYMENT_LOADER_KEY = new Key<>(QuarkusDeploymentSupport.class.getName());

public static CompletableFuture<Void> updateClasspathWithQuarkusDeployment(Module module) {
CompletableFuture<Void> loader = module.getUserData(QUARKUS_DEPLOYMENT_LOADER_KEY);
if (isOutOfDated(loader)) {
return updateClasspathWithQuarkusDeploymentSync(module);
}
return loader;
}

private static boolean isOutOfDated(CompletableFuture<Void> loader) {
return loader == null || loader.isCancelled() || loader.isCompletedExceptionally();
}

@NotNull
private synchronized static CompletableFuture<Void> updateClasspathWithQuarkusDeploymentSync(Module module) {
CompletableFuture<Void> loader = module.getUserData(QUARKUS_DEPLOYMENT_LOADER_KEY);
if (!isOutOfDated(loader)) {
return loader;
}
var project = module.getProject();
final CompletableFuture<Void> future = new CompletableFuture<>();
CompletableFuture.runAsync(() -> {
Runnable task = () -> ProgressManager.getInstance().run(new Task.Backgroundable(project, "Computing deployment jars...") {
@Override
public void run(@NotNull ProgressIndicator indicator) {
System.out.println("Computing deployment jars...");
try {
long start = System.currentTimeMillis();
QuarkusModuleUtil.ensureQuarkusLibrary(module, indicator);
long elapsed = System.currentTimeMillis() - start;
System.out.println("Ensured QuarkusLibrary in " + elapsed + " ms");
future.complete(null);
} catch (CancellationException | ProcessCanceledException e) {
future.cancel(true);
} catch (Exception e) {
if (module != null) {
}
future.completeExceptionally(e);
}
}
});
if (DumbService.getInstance(project).isDumb()) {
DumbService.getInstance(project).runWhenSmart(task);
} else {
task.run();
}
});
module.putUserData(QUARKUS_DEPLOYMENT_LOADER_KEY, future);
return future;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.redhat.devtools.intellij.lsp4mp4ij.settings.MicroProfileInspectionsInfo;
import com.redhat.devtools.intellij.lsp4mp4ij.settings.UserDefinedMicroProfileSettings;
import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil;
import com.redhat.devtools.intellij.quarkus.deployment.QuarkusDeploymentSupport;
import org.eclipse.lsp4j.*;
import org.eclipse.lsp4mp.commons.*;
import org.eclipse.lsp4mp.commons.codeaction.CodeActionResolveData;
Expand All @@ -41,6 +42,7 @@
import org.eclipse.lsp4mp.ls.api.MicroProfileLanguageServerAPI;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -144,11 +146,31 @@ private boolean isConfigSource(VirtualFile file) {

@Override
public CompletableFuture<MicroProfileProjectInfo> getProjectInfo(MicroProfileProjectInfoParams params) {
IPsiUtils utils = PsiUtilsLSImpl.getInstance(getProject());
VirtualFile file = null;
try {
file = utils.findFile(params.getUri());
} catch (IOException e) {
throw new RuntimeException(e);
}
Module module = utils.getModule(file);
if (module == null) {
throw new RuntimeException();
}
CompletableFuture<Void> loader = QuarkusDeploymentSupport.updateClasspathWithQuarkusDeployment(module);
if (loader.isDone()) {
return internalGetProjectInfo(params);
}
return loader
.thenCompose(unused -> internalGetProjectInfo(params));
}

private CompletableFuture<MicroProfileProjectInfo> internalGetProjectInfo(MicroProfileProjectInfoParams params) {
var coalesceBy = new CoalesceByKey("microprofile/projectInfo", params.getUri(), params.getScopes());
String filePath = getFilePath(params.getUri());
return runAsBackground("Computing MicroProfile properties for '" + filePath + "'.", monitor ->
PropertiesManager.getInstance().getMicroProfileProjectInfo(params, PsiUtilsLSImpl.getInstance(getProject()), monitor)
, coalesceBy);
PropertiesManager.getInstance().getMicroProfileProjectInfo(params, PsiUtilsLSImpl.getInstance(getProject()), monitor)
, coalesceBy);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ private List<MavenArtifact> ensureDownloaded(Module module, MavenProject mavenPr
}
} else {
List<MavenArtifactInfo> infos = deploymentIds.stream().map(id -> new MavenArtifactInfo(id, "jar", classifier)).collect(Collectors.toList());
result = serverWrapper.resolveTransitively(infos, mavenProject.getRemoteRepositories());
result = serverWrapper.resolveArtifactTransitively(infos, mavenProject.getRemoteRepositories()).mavenResolvedArtifacts;
}
} catch (MavenProcessCanceledException | RuntimeException e) {
LOGGER.warn(e.getLocalizedMessage(), e);
Expand Down

0 comments on commit 9c72598

Please sign in to comment.