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

Improve priming from LSP client #7063

Merged
merged 5 commits into from
Mar 25, 2024
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 @@ -52,6 +52,7 @@
import java.util.concurrent.CompletionException;
import java.util.stream.Collectors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import org.eclipse.lsp4j.CallHierarchyRegistrationOptions;
import org.eclipse.lsp4j.CodeActionKind;
Expand Down Expand Up @@ -644,13 +645,11 @@ private void asyncOpenSelectedProjects0(CompletableFuture<Project[]> f, List<Fil
f.completeExceptionally(ex);
}
}

private void asyncOpenSelectedProjects1(CompletableFuture<Project[]> f, Project[] previouslyOpened, Project[] candidateMapping, List<Project> projects, boolean addToWorkspace) {
int id = this.openRequestId.getAndIncrement();

List<CompletableFuture> primingBuilds = new ArrayList<>();

CompletableFuture<Void>[] primeProjects(Collection<Project> projects, int id, Map<Project, CompletableFuture<Void>> local) {
List<Project> toOpen = new ArrayList<>();
Map<Project, CompletableFuture<Void>> local = new HashMap<>();
List<CompletableFuture<Void>> primingBuilds = new ArrayList<>();

synchronized (this) {
LOG.log(Level.FINER, "{0}: Opening project(s): {1}", new Object[]{ id, Arrays.asList(projects) });
for (Project p : projects) {
Expand All @@ -664,7 +663,7 @@ private void asyncOpenSelectedProjects1(CompletableFuture<Project[]> f, Project[
}
beingOpened.putAll(local);
}
long t = System.currentTimeMillis();

LOG.log(Level.FINER, id + ": Opening projects: {0}", Arrays.asList(toOpen));

// before the projects are officialy 'opened', try to prime the projects
Expand Down Expand Up @@ -698,9 +697,20 @@ public void finished(boolean success) {
pap.invokeAction(ActionProvider.COMMAND_PRIME, Lookups.fixed(progress));
}
}
return primingBuilds.toArray(new CompletableFuture[0]);
}

private void asyncOpenSelectedProjects1(CompletableFuture<Project[]> f, Project[] previouslyOpened, Project[] candidateMapping, List<Project> initialProjects, boolean addToWorkspace) {
long t = System.currentTimeMillis();
int id = this.openRequestId.getAndIncrement();
Map<Project, CompletableFuture<Void>> local = new HashMap<>();

// Wait for all priming builds, even those already pending, to finish:
CompletableFuture.allOf(primingBuilds.toArray(new CompletableFuture[0])).thenRun(() -> {
CompletableFuture[] primingBuilds = primeProjects(initialProjects, id, local);

AtomicReference<Consumer<Collection<Project>>> subprojectProcessor = new AtomicReference();
Set<Project> processedProjects = new HashSet<>();
AtomicInteger level = new AtomicInteger(1);
subprojectProcessor.set((projects) -> {
Set<Project> additionalProjects = new LinkedHashSet<>();
for (Project prj : projects) {
Set<Project> containedProjects = ProjectUtils.getContainedProjects(prj, true);
Expand All @@ -709,53 +719,75 @@ public void finished(boolean success) {
additionalProjects.addAll(containedProjects);
}
}
projects.addAll(additionalProjects);
OpenProjects.getDefault().open(projects.toArray(new Project[0]), false);
try {
LOG.log(Level.FINER, "{0}: Calling openProjects() for : {1}", new Object[]{id, Arrays.asList(projects)});
OpenProjects.getDefault().openProjects().get();
} catch (InterruptedException | ExecutionException ex) {
throw new IllegalStateException(ex);
}
for (Project prj : projects) {
//init source groups/FileOwnerQuery:
ProjectUtils.getSources(prj).getSourceGroups(Sources.TYPE_GENERIC);
final CompletableFuture<Void> prjF = local.get(prj);
if (prjF != null) {
prjF.complete(null);
additionalProjects.removeAll(processedProjects);
additionalProjects.removeAll(projects);

processedProjects.addAll(projects);

LOG.log(Level.FINE, "Processing subprojects, level {0}: {1}", new Object[] { level.getAndIncrement(), additionalProjects });

if (additionalProjects.isEmpty()) {
OpenProjects.getDefault().open(processedProjects.toArray(new Project[processedProjects.size()]), false);
try {
LOG.log(Level.FINER, "{0}: Calling openProjects() for : {1}", new Object[]{id, Arrays.asList(processedProjects)});
OpenProjects.getDefault().openProjects().get();
} catch (InterruptedException | ExecutionException ex) {
throw new IllegalStateException(ex);
}
}
Set<Project> projectSet = new HashSet<>(Arrays.asList(OpenProjects.getDefault().getOpenProjects()));
projectSet.retainAll(openedProjects);
projectSet.addAll(projects);

Project[] prjsRequested = projects.toArray(new Project[0]);
Project[] prjs = projects.toArray(new Project[0]);
LOG.log(Level.FINER, "{0}: Finished opening projects: {1}", new Object[]{id, Arrays.asList(projects)});
synchronized (this) {
openedProjects = projectSet;
if (addToWorkspace) {
Set<Project> ns = new HashSet<>(projects);
List<Project> current = Arrays.asList(workspaceProjects.getNow(new Project[0]));
int s = current.size();
ns.addAll(current);
LOG.log(Level.FINER, "Current is: {0}, ns: {1}", new Object[] { current, ns });
if (s != ns.size()) {
prjs = ns.toArray(new Project[0]);
workspaceProjects = CompletableFuture.completedFuture(prjs);
for (Project prj : processedProjects) {
//init source groups/FileOwnerQuery:
ProjectUtils.getSources(prj).getSourceGroups(Sources.TYPE_GENERIC);
final CompletableFuture<Void> prjF = local.get(prj);
if (prjF != null) {
prjF.complete(null);
}
}
for (Project p : prjs) {
// override flag in opening cache, no further questions asked.
openingFileOwners.put(p, f.thenApply(unused -> p));
Set<Project> projectSet = new HashSet<>(Arrays.asList(OpenProjects.getDefault().getOpenProjects()));
projectSet.retainAll(openedProjects);
projectSet.addAll(processedProjects);

Project[] prjsRequested = projects.toArray(new Project[processedProjects.size()]);
Project[] prjs = projects.toArray(new Project[processedProjects.size()]);
LOG.log(Level.FINER, "{0}: Finished opening projects: {1}", new Object[]{id, Arrays.asList(processedProjects)});
synchronized (this) {
openedProjects = projectSet;
if (addToWorkspace) {
Set<Project> ns = new HashSet<>(processedProjects);
List<Project> current = Arrays.asList(workspaceProjects.getNow(new Project[0]));
int s = current.size();
ns.addAll(current);
LOG.log(Level.FINER, "Current is: {0}, ns: {1}", new Object[] { current, ns });
if (s != ns.size()) {
prjs = ns.toArray(new Project[ns.size()]);
workspaceProjects = CompletableFuture.completedFuture(prjs);
}
}
for (Project p : prjs) {
// override flag in opening cache, no further questions asked.
openingFileOwners.put(p, f.thenApply(unused -> p));
}
}
f.complete(candidateMapping);
LOG.log(Level.INFO, "{0} projects opened in {1}ms", new Object[] { prjsRequested.length, (System.currentTimeMillis() - t) });
} else {
LOG.log(Level.FINER, "{0}: Collecting projects to prime from: {1}", new Object[]{id, Arrays.asList(additionalProjects)});
CompletableFuture[] nextPrimingBuilds = primeProjects(additionalProjects, id, local);
CompletableFuture.allOf(nextPrimingBuilds).thenRun(() -> {
subprojectProcessor.get().accept(additionalProjects);
}).exceptionally(e -> {
f.completeExceptionally(e);
return null;
});
}
f.complete(candidateMapping);
LOG.log(Level.INFO, "{0} projects opened in {1}ms", new Object[] { prjsRequested.length, (System.currentTimeMillis() - t) });
});

// Wait for all priming builds, even those already pending, to finish:
CompletableFuture.allOf(primingBuilds).thenRun(() -> {
subprojectProcessor.get().accept(initialProjects);
}).exceptionally(e -> {
f.completeExceptionally(e);
return null;
});
});
}

private JavaSource checkJavaSupport() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,15 @@
import org.eclipse.aether.repository.Authentication;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.impl.VersionResolver;
import org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManagerFactory;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.NoLocalRepositoryManagerException;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
import org.eclipse.aether.util.repository.DefaultMirrorSelector;
import org.eclipse.aether.util.repository.DefaultProxySelector;
import org.netbeans.modules.maven.embedder.impl.NbVersionResolver2;

/**
* Handle for the embedded Maven system, used to parse POMs and more.
Expand All @@ -123,6 +125,7 @@ public final class MavenEmbedder {
private final SettingsBuilder settingsBuilder;
private final EmbedderConfiguration embedderConfiguration;
private final SettingsDecrypter settingsDecrypter;
private final NbVersionResolver2 versionResolver;
private long settingsTimestamp;
private static final Object lastLocalRepositoryLock = new Object();
private static URI lastLocalRepository;
Expand All @@ -137,6 +140,13 @@ public final class MavenEmbedder {
this.settingsBuilder = plexus.lookup(SettingsBuilder.class);
this.populator = plexus.lookup(MavenExecutionRequestPopulator.class);
settingsDecrypter = plexus.lookup(SettingsDecrypter.class);

VersionResolver vr = plexus.lookup(VersionResolver.class);
if (vr instanceof NbVersionResolver2) {
versionResolver = (NbVersionResolver2)vr;
} else {
versionResolver = null;
}
}

public PlexusContainer getPlexus() {
Expand Down Expand Up @@ -235,7 +245,7 @@ public MavenExecutionResult readProjectWithDependencies(MavenExecutionRequest re

public MavenExecutionResult readProjectWithDependencies(MavenExecutionRequest req, boolean useWorkspaceResolution) {
if (useWorkspaceResolution) {
req.setWorkspaceReader(new NbWorkspaceReader());
req.setWorkspaceReader(new NbWorkspaceReader(versionResolver));
}
File pomFile = req.getPom();
MavenExecutionResult result = new DefaultMavenExecutionResult();
Expand All @@ -258,7 +268,7 @@ public MavenExecutionResult readProjectWithDependencies(MavenExecutionRequest re

public List<MavenExecutionResult> readProjectsWithDependencies(MavenExecutionRequest req, List<File> poms, boolean useWorkspaceResolution) {
if (useWorkspaceResolution) {
req.setWorkspaceReader(new NbWorkspaceReader());
req.setWorkspaceReader(new NbWorkspaceReader(versionResolver));
}
// File pomFile = req.getPom();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@

import com.google.inject.Binder;
import com.google.inject.Module;
import com.google.inject.Scopes;
import org.apache.maven.model.building.ModelBuilder;
import org.apache.maven.plugin.internal.PluginDependenciesResolver;
import org.eclipse.aether.impl.ArtifactDescriptorReader;
import org.eclipse.aether.impl.VersionRangeResolver;
import org.eclipse.aether.impl.VersionResolver;
import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
Expand All @@ -48,6 +52,12 @@ public void configure(Binder binder) {
//exxperimental only.
// binder.bind(InheritanceAssembler.class).to(NbInheritanceAssembler.class);
binder.bind(ModelBuilder.class).to(NBModelBuilder.class);

// This allows to capture origin for version and artifact queries, so that ArtifactFixer can determine
// if a pom was really requested, or some other artifact's classifier/type was
binder.bind(VersionResolver.class).to(NbVersionResolver2.class).in(Scopes.SINGLETON);
binder.bind(VersionRangeResolver.class).to(NbVersionResolver2.class).in(Scopes.SINGLETON);
binder.bind(ArtifactDescriptorReader.class).to(NbVersionResolver2.class).in(Scopes.SINGLETON);
}

}
Loading
Loading