Skip to content

Commit

Permalink
Show with progress bar the load of long process
Browse files Browse the repository at this point in the history
Fixes eclipse#471

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr authored and vrubezhny committed Aug 3, 2023
1 parent a3c7d9c commit f366462
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -74,6 +73,8 @@
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.lemminx.commons.TextDocument;
import org.eclipse.lemminx.commons.progress.ProgressMonitor;
import org.eclipse.lemminx.commons.progress.ProgressSupport;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMParser;
import org.eclipse.lemminx.extensions.maven.participants.codeaction.ExtractPropertyCodeAction;
Expand Down Expand Up @@ -147,6 +148,8 @@ public class MavenLemminxExtension implements IXMLExtension {
// Thread which loads Maven component (plexus container, maven session, etc) which can take some time.
private CompletableFuture<Void> mavenInitializer;

private ProgressSupport progressSupport;

@Override
public void doSave(ISaveContext context) {
if (context.getType() == SaveContextType.SETTINGS) {
Expand Down Expand Up @@ -177,6 +180,7 @@ public void start(InitializeParams params, XMLExtensionsRegistry registry) {
}
this.currentRegistry = registry;
this.resolverExtensionManager = registry.getResolverExtensionManager();
this.progressSupport = registry.getProgressSupport();
try {
// Do not invoke getters the MavenLemminxExtension in participant constructors,
// or that will trigger loading of plexus, Maven and so on even for non pom files
Expand Down Expand Up @@ -207,7 +211,7 @@ private void initialize() throws MavenInitializationException {
}

private CompletableFuture<Void> getMavenInitializer() {
if (mavenInitializer != null) {
if (mavenInitializer != null && !mavenInitializer.isCompletedExceptionally()) {
return mavenInitializer;
}
return getOrCreateMavenInitializer();
Expand Down Expand Up @@ -236,49 +240,117 @@ private synchronized CompletableFuture<Void> getOrCreateMavenInitializer() {
}

private void doInitialize(CancelChecker cancelChecker) {
Exception error = null;
ProgressMonitor progressMonitor = progressSupport != null ? progressSupport.createProgressMonitor() : null;
try {
if (progressMonitor != null) {
progressMonitor.begin("Loading Maven components...", "", 100, null);
}
boolean skipCentralRepository = settings.getCentral().isSkip();
int nbSteps = 7 - (skipCentralRepository ? 1 : 0);
int currentStep = 1;
int percentage = 15;

// Step1 : initialize Plexus container
cancelChecker.checkCanceled();
if (progressMonitor != null) {
progressMonitor.report("Initializing Plexus container" + getStepMessage(currentStep, nbSteps) + "...",
percentage, null);
}
this.container = newPlexusContainer();

// Step2 : initialize Maven request
cancelChecker.checkCanceled();
if (progressMonitor != null) {
currentStep++;
percentage += 15;
progressMonitor.report("Initializing Maven request" + getStepMessage(currentStep, nbSteps) + "...",
percentage, null);
}
mavenRequest = initMavenRequest(container, settings);

// Step3 : initialize Repository system session
cancelChecker.checkCanceled();
DefaultRepositorySystemSessionFactory repositorySessionFactory = container.lookup(DefaultRepositorySystemSessionFactory.class);
RepositorySystemSession repositorySystemSession = repositorySessionFactory.newRepositorySession(mavenRequest);

if (progressMonitor != null) {
currentStep++;
percentage += 15;
progressMonitor.report(
"Initializing Repository system session" + getStepMessage(currentStep, nbSteps) + "...",
percentage, null);
}
DefaultRepositorySystemSessionFactory repositorySessionFactory = container
.lookup(DefaultRepositorySystemSessionFactory.class);
RepositorySystemSession repositorySystemSession = repositorySessionFactory
.newRepositorySession(mavenRequest);

// Step4 : initialize Maven session
cancelChecker.checkCanceled();
if (progressMonitor != null) {
currentStep++;
percentage += 15;
progressMonitor.report("Initializing Maven session" + getStepMessage(currentStep, nbSteps) + "...",
percentage, null);
}
MavenExecutionResult mavenResult = new DefaultMavenExecutionResult();
// TODO: MavenSession is deprecated. Investigate for alternative
mavenSession = new MavenSession(container, repositorySystemSession, mavenRequest, mavenResult);
cache = new MavenProjectCache(mavenSession);

// Step5 : create local repository searcher
cancelChecker.checkCanceled();
localRepositorySearcher = new LocalRepositorySearcher(mavenSession.getRepositorySession().getLocalRepository().getBasedir());

if (!settings.getCentral().isSkip()) {
if (progressMonitor != null) {
currentStep++;
percentage += 15;
progressMonitor.report(
"Creating local repository searcher" + getStepMessage(currentStep, nbSteps) + "...", percentage,
null);
}
localRepositorySearcher = new LocalRepositorySearcher(
mavenSession.getRepositorySession().getLocalRepository().getBasedir(), progressSupport);

if (!skipCentralRepository) {
// Step6 : create central repository searcher
cancelChecker.checkCanceled();
if (progressMonitor != null) {
currentStep++;
percentage += 15;
progressMonitor.report(
"Creating central repository searcher" + getStepMessage(currentStep, nbSteps) + "...",
percentage, null);
}
centralSearcher = new RemoteCentralRepositorySearcher();
}
buildPluginManager = null;
mavenPluginManager = container.lookup(MavenPluginManager.class);
buildPluginManager = container.lookup(BuildPluginManager.class);

// Step7 : initializing Workspace readers
cancelChecker.checkCanceled();
internalDidChangeWorkspaceFolders(this.initialWorkspaceFolders.stream().map(WorkspaceFolder::getUri).map(URI::create).toArray(URI[]::new), null);
if (progressMonitor != null) {
currentStep++;
percentage += 15;
progressMonitor.report("Initializing Workspace readers" + getStepMessage(currentStep, nbSteps) + "...",
percentage, null);
}
internalDidChangeWorkspaceFolders(this.initialWorkspaceFolders.stream().map(WorkspaceFolder::getUri)
.map(URI::create).toArray(URI[]::new), null);
} catch (Exception e) {
error = e;
LOGGER.log(Level.SEVERE, e.getMessage(), e);
stop(currentRegistry);
} finally {
if (progressMonitor != null) {
String message = error != null ? "Maven initialization terminated with error " + error.getMessage()
: "Maven initialization done";
progressMonitor.end(message);
}
}
}

private static String getStepMessage(int currentStep, int nbSteps) {
return " (" + currentStep + "/" + nbSteps + ")";
}

private MavenExecutionRequest initMavenRequest(PlexusContainer container, XMLMavenSettings options) throws Exception {
MavenExecutionRequest mavenRequest = new DefaultMavenExecutionRequest();
Properties systemProperties = getSystemProperties();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.ClosedWatchServiceException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -21,14 +22,15 @@
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
Expand All @@ -38,24 +40,33 @@
import org.apache.maven.model.Dependency;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.lemminx.commons.progress.ProgressMonitor;
import org.eclipse.lemminx.commons.progress.ProgressSupport;
import org.eclipse.lemminx.extensions.maven.MavenLemminxExtension;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
import org.eclipse.lsp4j.jsonrpc.CompletableFutures;

/**
* Search for collecting artifacts from the local repository /.m2
*
*/
public class LocalRepositorySearcher {

private static final Logger LOGGER = Logger.getLogger(LocalRepositorySearcher.class.getName());

private final File localRepository;

public LocalRepositorySearcher(File localRepository) {
this.localRepository = localRepository;
}
private final ProgressSupport progressSupport;

private Map<File, CompletableFuture<Collection<Artifact>>> cache = new HashMap<>();
private WatchKey watchKey;
private WatchService watchService;

public LocalRepositorySearcher(File localRepository, ProgressSupport progressSupport) {
this.localRepository = localRepository;
this.progressSupport = progressSupport;
}

public Set<String> searchGroupIds() throws IOException {
return getLocalArtifactsLastVersion().stream().map(Artifact::getGroupId).distinct().collect(Collectors.toSet());
}
Expand Down Expand Up @@ -134,8 +145,38 @@ private synchronized CompletableFuture<Collection<Artifact>> getOrCreateLocalArt

private Collection<Artifact> computeLocalArtifacts(CancelChecker cancelChecker) throws IOException {
final Path repoPath = localRepository.toPath();
ProgressMonitor progressMonitor = progressSupport != null ? progressSupport.createProgressMonitor() : null;
if (progressMonitor != null) {
progressMonitor.begin("Loading local artifacts from '" + repoPath, null, 100, null);
}
Map<String, Artifact> groupIdArtifactIdToVersion = new HashMap<>();
Files.walkFileTree(repoPath, Collections.emptySet(), 10, new SimpleFileVisitor<Path>() {
try {
List<Path> subPaths = getSubDirectories(repoPath);
int increment = Math.round(100f / subPaths.size());
int i = 0;
for (Path path : subPaths) {
cancelChecker.checkCanceled();
if (progressMonitor != null) {
progressMonitor.report("scanning folder' " + path.getFileName().getName(0) + "' (" + i++ + "/" + subPaths.size() + ")...", increment++, null);
}
try {
collect(path, repoPath, groupIdArtifactIdToVersion, cancelChecker);
}
catch(IOException e) {
LOGGER.log(Level.SEVERE, "Error while scanning local repo folder " + path, e);
}
}
} finally {
if (progressMonitor != null) {
progressMonitor.end(null);
}
}
return groupIdArtifactIdToVersion.values();
}

private void collect(final Path currentPath, final Path repoPath, Map<String, Artifact> groupIdArtifactIdToVersion,
CancelChecker cancelChecker) throws IOException {
Files.walkFileTree(currentPath, Collections.emptySet(), 10, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path file, BasicFileAttributes attrs) throws IOException {
if (file.getFileName().toString().charAt(0) == '.') {
Expand Down Expand Up @@ -174,7 +215,20 @@ public FileVisitResult preVisitDirectory(Path file, BasicFileAttributes attrs) t
return FileVisitResult.SKIP_SUBTREE;
}
});
return groupIdArtifactIdToVersion.values();
}

private static List<Path> getSubDirectories(Path directoryPath) {
List<Path> subDirectories = new ArrayList<>();
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directoryPath)) {
for (Path entry : directoryStream) {
if (Files.isDirectory(entry)) {
subDirectories.add(entry);
}
}
} catch (IOException e) {
// Do nothing
}
return subDirectories;
}

// TODO consider using directly ArtifactRepository for those 2 methods
Expand All @@ -195,7 +249,7 @@ public void stop() {
cache
.values()
.forEach(f -> f.cancel(true));
// Close the watch service which tracks the local repository.
// Close the watch service which tracks the local repository.
if (watchService != null && watchKey != null) {
watchKey.cancel();
try {
Expand Down

0 comments on commit f366462

Please sign in to comment.