From daa9625a94d160c426aa366ec31980d454ac12b0 Mon Sep 17 00:00:00 2001 From: azerr Date: Sat, 29 Jul 2023 13:12:34 +0200 Subject: [PATCH] Use CompletableFuture.completableValue in Junit Test mode Signed-off-by: azerr --- .../maven/MavenLemminxExtension.java | 101 ++++++++++++------ .../searcher/LocalRepositorySearcher.java | 79 ++++++++------ 2 files changed, 116 insertions(+), 64 deletions(-) diff --git a/lemminx-maven/src/main/java/org/eclipse/lemminx/extensions/maven/MavenLemminxExtension.java b/lemminx-maven/src/main/java/org/eclipse/lemminx/extensions/maven/MavenLemminxExtension.java index 3b7e2910..2353bfe3 100644 --- a/lemminx-maven/src/main/java/org/eclipse/lemminx/extensions/maven/MavenLemminxExtension.java +++ b/lemminx-maven/src/main/java/org/eclipse/lemminx/extensions/maven/MavenLemminxExtension.java @@ -104,6 +104,8 @@ import org.eclipse.lemminx.uriresolver.URIResolverExtensionManager; import org.eclipse.lsp4j.InitializeParams; import org.eclipse.lsp4j.WorkspaceFolder; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.eclipse.lsp4j.jsonrpc.CompletableFutures; /** * Extension for pom.xml. @@ -204,44 +206,77 @@ private void initialize() throws MavenInitializationException { } } - private synchronized CompletableFuture getMavenInitializer() { - // Create thread which loads Maven component (plexus container, maven session, etc) which can take some time. - // We do this initialization on background to avoid breaking the XML syntax validation, XML based onXSD, XML completion based on XSD + private CompletableFuture getMavenInitializer() { + if (mavenInitializer != null) { + return mavenInitializer; + } + return getOrCreateMavenInitializer(); + } + + private synchronized CompletableFuture getOrCreateMavenInitializer() { + if (mavenInitializer != null) { + return mavenInitializer; + } + // Create thread which loads Maven component (plexus container, maven session, + // etc) which can take some time. + // We do this initialization on background to avoid breaking the XML syntax + // validation, XML based onXSD, XML completion based on XSD // while Maven component is initializing. if (mavenInitializer == null) { - mavenInitializer = CompletableFuture.runAsync(() -> { - try { - this.container = newPlexusContainer(); - mavenRequest = initMavenRequest(container, settings); - DefaultRepositorySystemSessionFactory repositorySessionFactory = container.lookup(DefaultRepositorySystemSessionFactory.class); - RepositorySystemSession repositorySystemSession = repositorySessionFactory.newRepositorySession(mavenRequest); - MavenExecutionResult mavenResult = new DefaultMavenExecutionResult(); - // TODO: MavenSession is deprecated. Investigate for alternative - mavenSession = new MavenSession(container, repositorySystemSession, mavenRequest, mavenResult); - cache = new MavenProjectCache(mavenSession); - localRepositorySearcher = new LocalRepositorySearcher(mavenSession.getRepositorySession().getLocalRepository().getBasedir()); - if (!settings.getCentral().isSkip()) { - centralSearcher = new RemoteCentralRepositorySearcher(); - } - buildPluginManager = null; - mavenPluginManager = container.lookup(MavenPluginManager.class); - buildPluginManager = container.lookup(BuildPluginManager.class); - internalDidChangeWorkspaceFolders(this.initialWorkspaceFolders.stream().map(WorkspaceFolder::getUri).map(URI::create).toArray(URI[]::new), null); - } catch (Exception e) { - LOGGER.log(Level.SEVERE, e.getMessage(), e); - stop(currentRegistry); - } - }); + if (isUnitTestMode()) { + doInitialize(() -> {}); + mavenInitializer = CompletableFuture.completedFuture(null); + } else + mavenInitializer = CompletableFutures.computeAsync(cancelChecker -> { + doInitialize(cancelChecker); + return null; + }); } - if (isUnitTestMode()) { - // This code is for tests - try { - mavenInitializer.get(); - } catch (InterruptedException | ExecutionException e) { - Thread.currentThread().interrupt(); + return mavenInitializer; + } + + private void doInitialize(CancelChecker cancelChecker) { + try { + // Step1 : initialize Plexus container + cancelChecker.checkCanceled(); + this.container = newPlexusContainer(); + + // Step2 : initialize Maven request + cancelChecker.checkCanceled(); + mavenRequest = initMavenRequest(container, settings); + + // Step3 : initialize Repository system session + cancelChecker.checkCanceled(); + DefaultRepositorySystemSessionFactory repositorySessionFactory = container.lookup(DefaultRepositorySystemSessionFactory.class); + RepositorySystemSession repositorySystemSession = repositorySessionFactory.newRepositorySession(mavenRequest); + + // Step4 : initialize Maven session + cancelChecker.checkCanceled(); + 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()) { + // Step6 : create central repository searcher + cancelChecker.checkCanceled(); + 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); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, e.getMessage(), e); + stop(currentRegistry); } - return mavenInitializer; } private MavenExecutionRequest initMavenRequest(PlexusContainer container, XMLMavenSettings options) throws Exception { diff --git a/lemminx-maven/src/main/java/org/eclipse/lemminx/extensions/maven/searcher/LocalRepositorySearcher.java b/lemminx-maven/src/main/java/org/eclipse/lemminx/extensions/maven/searcher/LocalRepositorySearcher.java index 7eb89197..eed8392a 100644 --- a/lemminx-maven/src/main/java/org/eclipse/lemminx/extensions/maven/searcher/LocalRepositorySearcher.java +++ b/lemminx-maven/src/main/java/org/eclipse/lemminx/extensions/maven/searcher/LocalRepositorySearcher.java @@ -39,6 +39,8 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.lemminx.extensions.maven.MavenLemminxExtension; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.eclipse.lsp4j.jsonrpc.CompletableFutures; public class LocalRepositorySearcher { @@ -69,71 +71,84 @@ public Collection getLocalPluginArtifacts() { public Collection getLocalArtifactsLastVersion() { CompletableFuture> loadLocalArtifacts = cache.get(localRepository); if (loadLocalArtifacts == null || loadLocalArtifacts.isCompletedExceptionally()) { - // Load local artifacts on background - loadLocalArtifacts = CompletableFuture.supplyAsync(() -> { + loadLocalArtifacts = getOrCreateLocalArtifactsLastVersion(); + } + if (loadLocalArtifacts.isDone()) { + return loadLocalArtifacts.getNow(Collections.emptyList()); + } + // The local artifacts search is not finished, returns an empty list + return Collections.emptyList(); + } + + private synchronized CompletableFuture> getOrCreateLocalArtifactsLastVersion() { + CompletableFuture> loadLocalArtifacts = cache.get(localRepository); + if (loadLocalArtifacts == null || loadLocalArtifacts.isCompletedExceptionally()) { + if (MavenLemminxExtension.isUnitTestMode()) { try { - return computeLocalArtifacts(); + loadLocalArtifacts = CompletableFuture.completedFuture(computeLocalArtifacts(() -> {})); } catch (IOException e) { - throw new CompletionException(e); + LOGGER.log(Level.SEVERE, "Local repo loading error", e); } - }); - + } else { + // Load local artifacts on background + loadLocalArtifacts = CompletableFutures.computeAsync(cancelChecker -> { + try { + return computeLocalArtifacts(cancelChecker); + } catch (IOException e) { + throw new CompletionException(e); + } + }); + } + // Update the cache cache.put(localRepository, loadLocalArtifacts); - - if (MavenLemminxExtension.isUnitTestMode()) { - try { - loadLocalArtifacts.get(); - } catch (InterruptedException | ExecutionException e) { - Thread.currentThread().interrupt(); - } - } - - // Once the local artifacts are loaded, we track the local repository to update the cache. - loadLocalArtifacts.thenAcceptAsync(artifacts -> { + + // Once the local artifacts are loaded, we track the local repository to update + // the cache. + loadLocalArtifacts.thenAcceptAsync(artifacts -> { try { - Path localRepoPath = localRepository.toPath(); - watchService = localRepoPath.getFileSystem().newWatchService(); - watchKey = localRepoPath.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE); - - WatchKey key; - + Path localRepoPath = localRepository.toPath(); + watchService = localRepoPath.getFileSystem().newWatchService(); + watchKey = localRepoPath.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, + StandardWatchEventKinds.ENTRY_DELETE); + + WatchKey key; + while ((key = (watchService != null ? watchService.take() : null)) != null) { if (watchKey.equals(key)) { cache.remove(localRepository); key.reset(); } } - } catch(IOException e) { + } catch (IOException e) { LOGGER.log(Level.SEVERE, "Local repo thread watcher error", e); } catch (ClosedWatchServiceException e) { LOGGER.log(Level.WARNING, "Local repo thread watcher is closed"); } catch (InterruptedException e) { LOGGER.log(Level.SEVERE, "Local repo thread watcher interrupted", e); } - }); - } - if (loadLocalArtifacts.isDone()) { - return loadLocalArtifacts.getNow(Collections.emptyList()); + }); } - // The local artifacts search is not finished, returns an empty list - return Collections.emptyList(); + return loadLocalArtifacts; } - public Collection computeLocalArtifacts() throws IOException { + private Collection computeLocalArtifacts(CancelChecker cancelChecker) throws IOException { final Path repoPath = localRepository.toPath(); Map groupIdArtifactIdToVersion = new HashMap<>(); Files.walkFileTree(repoPath, Collections.emptySet(), 10, new SimpleFileVisitor() { @Override public FileVisitResult preVisitDirectory(Path file, BasicFileAttributes attrs) throws IOException { if (file.getFileName().toString().charAt(0) == '.') { + cancelChecker.checkCanceled(); return FileVisitResult.SKIP_SUBTREE; } if (!Character.isDigit(file.getFileName().toString().charAt(0))) { + cancelChecker.checkCanceled(); return FileVisitResult.CONTINUE; } Path artifactFolderPath = repoPath.relativize(file); if (artifactFolderPath.getNameCount() < 3) { + cancelChecker.checkCanceled(); // eg "maven-dependency-plugin/3.1.2" return FileVisitResult.SKIP_SUBTREE; } @@ -141,6 +156,7 @@ public FileVisitResult preVisitDirectory(Path file, BasicFileAttributes attrs) t String artifactId = artifactFolderPath.getParent().getFileName().toString(); String groupId = artifactFolderPath.getParent().getParent().toString().replace(artifactFolderPath.getFileSystem().getSeparator(), "."); if (!new File(file.toFile(), artifactId + '-' + version.toString() + ".pom").isFile()) { + cancelChecker.checkCanceled(); return FileVisitResult.SKIP_SUBTREE; } String groupIdArtifactId = groupId + ':' + artifactId; @@ -154,6 +170,7 @@ public FileVisitResult preVisitDirectory(Path file, BasicFileAttributes attrs) t if (replace) { groupIdArtifactIdToVersion.put(groupIdArtifactId, new DefaultArtifact(groupId, artifactId, null, version.toString())); } + cancelChecker.checkCanceled(); return FileVisitResult.SKIP_SUBTREE; } });