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

Use CompletableFuture.completableValue in Junit Test mode #472

Merged
merged 1 commit into from
Jul 29, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -204,44 +206,77 @@ private void initialize() throws MavenInitializationException {
}
}

private synchronized CompletableFuture<Void> 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<Void> getMavenInitializer() {
if (mavenInitializer != null) {
return mavenInitializer;
}
return getOrCreateMavenInitializer();
}

private synchronized CompletableFuture<Void> 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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -69,78 +71,92 @@ public Collection<Artifact> getLocalPluginArtifacts() {
public Collection<Artifact> getLocalArtifactsLastVersion() {
CompletableFuture<Collection<Artifact>> 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<Collection<Artifact>> getOrCreateLocalArtifactsLastVersion() {
CompletableFuture<Collection<Artifact>> 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);
vrubezhny marked this conversation as resolved.
Show resolved Hide resolved
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<Artifact> computeLocalArtifacts() throws IOException {
private Collection<Artifact> computeLocalArtifacts(CancelChecker cancelChecker) throws IOException {
final Path repoPath = localRepository.toPath();
Map<String, Artifact> groupIdArtifactIdToVersion = new HashMap<>();
Files.walkFileTree(repoPath, Collections.emptySet(), 10, new SimpleFileVisitor<Path>() {
@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;
}
ArtifactVersion version = new DefaultArtifactVersion(artifactFolderPath.getFileName().toString());
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;
Expand All @@ -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;
}
});
Expand Down