Change GAV computation for TestJars

This commit is contained in:
aboyko
2024-06-03 14:36:41 -04:00
parent d0270b8688
commit ba6fc2ac1b
25 changed files with 370 additions and 1358 deletions

View File

@@ -148,10 +148,10 @@ class TestJarLaunchListener implements ILaunchesListener2 {
private CompletableFuture<Map<String, String>> getTestJarArtifactsMap() {
try {
Optional<?> opt = BootLsCommandUtils.executeCommand(TypeToken.getParameterized(List.class, ExecutableProject.class), "sts/spring-boot/executableBootProjects").get(3, TimeUnit.SECONDS);
Optional<?> opt = BootLsCommandUtils.executeCommand(TypeToken.getParameterized(List.class, ExecutableProject.class), "sts/spring-boot/executableBootProjects").get(20, TimeUnit.SECONDS);
if (opt.isPresent() && opt.get() instanceof List<?> projects) {
Map<String, String> gavToFile = new ConcurrentHashMap<>();
CompletableFuture<?>[] futures = projects.stream().map(ExecutableProject.class::cast).map(p -> CompletableFuture.runAsync(() -> {
CompletableFuture<?>[] futures = projects.stream().map(ExecutableProject.class::cast).filter(p -> p.gav() != null).map(p -> CompletableFuture.runAsync(() -> {
try {
Path file = Files.createTempFile("%s_".formatted(p.gav().replace(":", "_")), UUID.randomUUID().toString());
Files.write(file, List.of(

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017, 2023 Pivotal, Inc.
* Copyright (c) 2017, 2024 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -19,6 +19,10 @@ import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.eclipse.core.resources.IFile;
@@ -72,15 +76,18 @@ import org.springframework.ide.vscode.commons.protocol.LiveProcessLoggersSummary
import org.springframework.ide.vscode.commons.protocol.LiveProcessSummary;
import org.springframework.ide.vscode.commons.protocol.STS4LanguageClient;
import org.springframework.ide.vscode.commons.protocol.java.ClasspathListenerParams;
import org.springframework.ide.vscode.commons.protocol.java.Gav;
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteData;
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteParams;
import org.springframework.ide.vscode.commons.protocol.java.JavaDataParams;
import org.springframework.ide.vscode.commons.protocol.java.JavaSearchParams;
import org.springframework.ide.vscode.commons.protocol.java.JavaTypeHierarchyParams;
import org.springframework.ide.vscode.commons.protocol.java.ProjectGavParams;
import org.springframework.ide.vscode.commons.protocol.java.TypeData;
import org.springframework.ide.vscode.commons.protocol.java.TypeDescriptorData;
import org.springframework.tooling.jdt.ls.commons.Logger;
import org.springframework.tooling.jdt.ls.commons.classpath.ReusableClasspathListenerHandler;
import org.springframework.tooling.jdt.ls.commons.java.BuildInfo;
import org.springframework.tooling.jdt.ls.commons.java.JavaCodeCompletion;
import org.springframework.tooling.jdt.ls.commons.java.JavaData;
import org.springframework.tooling.jdt.ls.commons.java.JavaFluxSearch;
@@ -96,6 +103,8 @@ import com.google.common.collect.ImmutableMap;
@SuppressWarnings("restriction")
public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4LanguageClient {
private final Executor executor;
public static final ReusableClasspathListenerHandler CLASSPATH_SERVICE = new ReusableClasspathListenerHandler (
Logger.forEclipsePlugin(LanguageServerCommonsActivator::getInstance),
new LSP4ECommandExecutor(),
@@ -413,6 +422,7 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La
public STS4LanguageClientImpl() {
this.executor = new ThreadPoolExecutor(1, 5, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
CLASSPATH_SERVICE.addNotificationsSentCallback(projectNames -> {
List<IProject> projects = projectNames.stream().map(projectName -> ResourcesPlugin.getWorkspace().getRoot().getProject(projectName)).filter(Objects::nonNull).collect(Collectors.toList());
for (IWorkbenchWindow ww : PlatformUI.getWorkbench().getWorkbenchWindows()) {
@@ -631,4 +641,9 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La
// Nothing to do for now
}
@Override
public CompletableFuture<List<Gav>> projectGAV(ProjectGavParams params) {
return BuildInfo.projectGAV(params, executor, Logger.forEclipsePlugin(LanguageServerCommonsActivator::getInstance));
}
}

View File

@@ -35,7 +35,7 @@ public class GradleJavaProject extends LegacyJavaProject {
private static final Logger log = LoggerFactory.getLogger(GradleJavaProject.class);
private GradleJavaProject(FileObserver fileObserver, Path projectDataCache, IClasspath classpath, File gradleFile, JavadocService javadocService) {
super(fileObserver, gradleFile.getParentFile().toURI(), projectDataCache, classpath, javadocService, IProjectBuild.create(ProjectBuild.GRADLE_PROJECT_TYPE, gradleFile.toURI(), null));
super(fileObserver, gradleFile.getParentFile().toURI(), projectDataCache, classpath, javadocService, IProjectBuild.create(ProjectBuild.GRADLE_PROJECT_TYPE, gradleFile.toURI()));
}
public static GradleJavaProject create(FileObserver fileObserver, GradleCore gradle, File gradleFile, Path projectDataCache, JavadocService javadocService) {

View File

@@ -12,17 +12,13 @@ package org.springframework.ide.vscode.commons.java;
import java.net.URI;
import org.springframework.ide.vscode.commons.protocol.java.Gav;
public interface IProjectBuild {
String getType();
URI getBuildFile();
IGav getGav();
static IProjectBuild create(String type, URI buildFile, Gav gav) {
static IProjectBuild create(String type, URI buildFile) {
return new IProjectBuild() {
@Override
@@ -35,9 +31,6 @@ public interface IProjectBuild {
return buildFile;
}
public IGav getGav() {
return IGav.create(gav);
}
};
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019, 2023 Pivotal, Inc.
* Copyright (c) 2019, 2024 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -21,11 +21,13 @@ import org.eclipse.lsp4j.jsonrpc.services.JsonNotification;
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
import org.eclipse.lsp4j.services.LanguageClient;
import org.springframework.ide.vscode.commons.protocol.java.ClasspathListenerParams;
import org.springframework.ide.vscode.commons.protocol.java.Gav;
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteData;
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteParams;
import org.springframework.ide.vscode.commons.protocol.java.JavaDataParams;
import org.springframework.ide.vscode.commons.protocol.java.JavaSearchParams;
import org.springframework.ide.vscode.commons.protocol.java.JavaTypeHierarchyParams;
import org.springframework.ide.vscode.commons.protocol.java.ProjectGavParams;
import org.springframework.ide.vscode.commons.protocol.java.TypeData;
import org.springframework.ide.vscode.commons.protocol.java.TypeDescriptorData;
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexLanguageClient;
@@ -96,4 +98,7 @@ public interface STS4LanguageClient extends LanguageClient, SpringIndexLanguageC
@JsonRequest("sts/javaCodeComplete")
CompletableFuture<List<JavaCodeCompleteData>> javaCodeComplete(JavaCodeCompleteParams params);
@JsonRequest("sts/project/gav")
CompletableFuture<List<Gav>> projectGAV(ProjectGavParams params);
}

View File

@@ -10,22 +10,22 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.protocol.java;
public record ProjectBuild(String type, String buildFile, Gav gav) {
public record ProjectBuild(String type, String buildFile) {
public static final String MAVEN_PROJECT_TYPE = "maven";
public static final String GRADLE_PROJECT_TYPE = "gradle";
public static ProjectBuild createMavenBuild(String buildFile, Gav gav) {
return new ProjectBuild(MAVEN_PROJECT_TYPE, buildFile, gav);
public static ProjectBuild createMavenBuild(String buildFile) {
return new ProjectBuild(MAVEN_PROJECT_TYPE, buildFile);
}
public static ProjectBuild createGradleBuild(String buildFile, Gav gav) {
return new ProjectBuild(GRADLE_PROJECT_TYPE, buildFile, gav);
public static ProjectBuild createGradleBuild(String buildFile) {
return new ProjectBuild(GRADLE_PROJECT_TYPE, buildFile);
}
@Override
public String toString() {
return "ProjectBuild [type=" + type + ", buildFile=" + buildFile + "gav=" + gav + "]";
return "ProjectBuild [type=" + type + ", buildFile=" + buildFile + "]";
}

View File

@@ -0,0 +1,17 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.protocol.java;
import java.util.List;
public record ProjectGavParams(List<String> projectUris) {
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016, 2019 Pivotal, Inc.
* Copyright (c) 2016, 2024 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -55,6 +55,7 @@ import org.eclipse.aether.util.graph.visitor.FilteringDependencyVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.java.JavaUtils;
import org.springframework.ide.vscode.commons.protocol.java.Gav;
/**
* Maven Core functionality
@@ -325,5 +326,17 @@ public class MavenCore {
}
return null;
}
public Gav computeGav(File pom) {
try {
MavenProject p = readProject(pom, false);
return new Gav(p.getGroupId(), p.getArtifactId(), p.getVersion());
} catch (Exception e) {
log.error("", e);
return null;
}
}
}

View File

@@ -13,7 +13,6 @@ package org.springframework.ide.vscode.commons.maven.java;
import java.io.File;
import java.nio.file.Path;
import org.apache.maven.project.MavenProject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.java.ClasspathFileBasedCache;
@@ -23,13 +22,9 @@ import org.springframework.ide.vscode.commons.java.IProjectBuild;
import org.springframework.ide.vscode.commons.java.LegacyJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.JavadocService;
import org.springframework.ide.vscode.commons.maven.MavenCore;
import org.springframework.ide.vscode.commons.protocol.java.Gav;
import org.springframework.ide.vscode.commons.protocol.java.ProjectBuild;
import org.springframework.ide.vscode.commons.util.FileObserver;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
/**
* Wrapper for Maven Core project
*
@@ -42,12 +37,9 @@ public class MavenJavaProject extends LegacyJavaProject {
private final File pom;
private Supplier<Gav> gavSupplier;
private MavenJavaProject(FileObserver fileObserver, Path projectDataCache, IClasspath classpath, File pom, JavadocService javadocService, Supplier<Gav> gavSupplier) {
super(fileObserver, pom.getParentFile().toURI(), projectDataCache, classpath, javadocService, IProjectBuild.create(ProjectBuild.MAVEN_PROJECT_TYPE, pom.toURI(), null));
private MavenJavaProject(FileObserver fileObserver, Path projectDataCache, IClasspath classpath, File pom, JavadocService javadocService) {
super(fileObserver, pom.getParentFile().toURI(), projectDataCache, classpath, javadocService, IProjectBuild.create(ProjectBuild.MAVEN_PROJECT_TYPE, pom.toURI()));
this.pom = pom;
this.gavSupplier = gavSupplier;
}
public static MavenJavaProject create(FileObserver fileObserver, MavenCore maven, File pom, Path projectDataCache, JavadocService javadocService) {
@@ -59,17 +51,7 @@ public class MavenJavaProject extends LegacyJavaProject {
() -> new MavenProjectClasspath(maven, pom),
fileBasedCache
);
return new MavenJavaProject(fileObserver, projectDataCache, classpath, pom, javadocService, Suppliers.memoize(() -> {
try {
MavenProject p = maven.readProject(pom, false);
return new Gav(p.getGroupId(), p.getArtifactId(), p.getVersion());
} catch (Exception e) {
log.error("", e);
return null;
}
}));
return new MavenJavaProject(fileObserver, projectDataCache, classpath, pom, javadocService);
}
public static MavenJavaProject create(FileObserver fileObserver, MavenCore maven, File pom, JavadocService javadocService) {
@@ -104,7 +86,7 @@ public class MavenJavaProject extends LegacyJavaProject {
@Override
public IProjectBuild getProjectBuild() {
return IProjectBuild.create(ProjectBuild.MAVEN_PROJECT_TYPE, pom.toURI(), gavSupplier.get());
return IProjectBuild.create(ProjectBuild.MAVEN_PROJECT_TYPE, pom.toURI());
}
}

View File

@@ -36,6 +36,7 @@ import java.util.Random;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.assertj.core.api.Condition;
@@ -120,11 +121,13 @@ import org.springframework.ide.vscode.commons.protocol.LiveProcessLoggersSummary
import org.springframework.ide.vscode.commons.protocol.LiveProcessSummary;
import org.springframework.ide.vscode.commons.protocol.STS4LanguageClient;
import org.springframework.ide.vscode.commons.protocol.java.ClasspathListenerParams;
import org.springframework.ide.vscode.commons.protocol.java.Gav;
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteData;
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteParams;
import org.springframework.ide.vscode.commons.protocol.java.JavaDataParams;
import org.springframework.ide.vscode.commons.protocol.java.JavaSearchParams;
import org.springframework.ide.vscode.commons.protocol.java.JavaTypeHierarchyParams;
import org.springframework.ide.vscode.commons.protocol.java.ProjectGavParams;
import org.springframework.ide.vscode.commons.protocol.java.TypeData;
import org.springframework.ide.vscode.commons.protocol.java.TypeDescriptorData;
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
@@ -155,6 +158,8 @@ public class LanguageServerHarness {
private final SimpleLanguageServer server;
private InitializeResult initResult;
private Function<File, Gav> gavSupplier;
private Map<String,TextDocumentInfo> documents = new HashMap<>();
private Multimap<String, CompletableFuture<HighlightParams>> highlights = MultimapBuilder.hashKeys().linkedListValues().build();
@@ -263,6 +268,10 @@ public class LanguageServerHarness {
intialize(null);
}
}
public void setGavSupplier(Function<File, Gav> s) {
this.gavSupplier = s;
}
public InitializeResult intialize(File workspaceRoot) throws Exception {
int parentPid = random.nextInt(40000)+1000;
@@ -449,6 +458,21 @@ public class LanguageServerHarness {
return CompletableFuture.completedFuture(new ShowDocumentResult(true));
}
@Override
public CompletableFuture<List<Gav>> projectGAV(ProjectGavParams params) {
List<Gav> gavs = new ArrayList<>(params.projectUris().size());
for (int i = 0; i < params.projectUris().size(); i++) {
gavs.add(null);
}
for (int i = 0; i < params.projectUris().size(); i++) {
Path path = Paths.get(URI.create(params.projectUris().get(i)));
if (gavSupplier != null) {
gavs.set(i, gavSupplier.apply(path.toFile()));
}
}
return CompletableFuture.completedFuture(gavs);
}
});
}

View File

@@ -15,7 +15,9 @@ Require-Bundle: org.eclipse.core.runtime,
io.projectreactor.reactor-core,
org.reactivestreams.reactive-streams,
org.eclipse.m2e.core,
org.eclipse.buildship.core
org.eclipse.buildship.core,
org.springframework.tooling.gradle,
org.eclipse.m2e.maven.runtime
Export-Package: org.springframework.ide.vscode.commons.protocol,
org.springframework.ide.vscode.commons.protocol.java,
org.springframework.ide.vscode.commons.protocol.spring,

View File

@@ -38,11 +38,9 @@ import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaProject;
import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.embedder.ArtifactKey;
import org.eclipse.m2e.core.project.IMavenProjectFacade;
import org.springframework.ide.vscode.commons.protocol.java.Classpath;
import org.springframework.ide.vscode.commons.protocol.java.Classpath.CPE;
import org.springframework.ide.vscode.commons.protocol.java.Gav;
import org.springframework.ide.vscode.commons.protocol.java.ProjectBuild;
import org.springframework.tooling.jdt.ls.commons.Logger;
@@ -232,23 +230,17 @@ public class ClasspathUtil {
final Path home = System.getProperty("user.home") == null ? null : new File(System.getProperty("user.home")).toPath();
if (MavenPlugin.isMavenProject(jp.getProject())) {
IMavenProjectFacade facade = MavenPlugin.getMavenProjectRegistry().getProject(jp.getProject());
ArtifactKey artifact = facade.getArtifactKey();
return ProjectBuild.createMavenBuild(facade.getPom().getLocationURI().toASCIIString(), new Gav(artifact.groupId(), artifact.artifactId(), artifact.version()));
if (facade != null) {
return ProjectBuild.createMavenBuild(facade.getPom().getLocationURI().toASCIIString());
} else {
return ProjectBuild.createMavenBuild(jp.getProject().getFile("pom.xml").getLocationURI().toASCIIString());
}
} else if (GradleProjectNature.isPresentOn(jp.getProject())) {
IFile g = jp.getProject().getFile("build.gradle");
if (!g.exists()) {
g = jp.getProject().getFile("build.gradle.kts");
}
// TODO: TestJars support requires GAV of a project. To be added properly later as it needs optimizations
// Optional<Gav> optGav = GradleCore.getWorkspace().getBuild(jp.getProject()).flatMap(build -> {
// try {
// return Optional.of(build.withConnection(conn -> StsGradleToolingModelBuilder.getModelBuilder(conn, jp.getProject().getLocation().toFile(), null).get(), new NullProgressMonitor()));
// } catch (Exception e) {
// logger.log(e);
// return Optional.empty();
// }
// }).map(m -> new Gav(m.group(), m.artifact(), m.version()));
return ProjectBuild.createGradleBuild(g.exists() ? g.getLocationURI().toASCIIString() : null, /*optGav.orElse(null)*/null);
return ProjectBuild.createGradleBuild(g.exists() ? g.getLocationURI().toASCIIString() : null);
} else {
try {
for (IClasspathEntry e : jp.getRawClasspath()) {
@@ -268,7 +260,7 @@ public class ClasspathUtil {
if (likelyMaven) {
IFile f = jp.getProject().getFile("pom.xml");
if (f.exists()) {
return ProjectBuild.createMavenBuild(f.getLocationURI().toASCIIString(), null);
return ProjectBuild.createMavenBuild(f.getLocationURI().toASCIIString());
}
} else if (likelyGradle) {
IFile g = jp.getProject().getFile("build.gradle");
@@ -276,12 +268,12 @@ public class ClasspathUtil {
g = jp.getProject().getFile("build.gradle.kts");
}
if (g.exists()) {
return ProjectBuild.createGradleBuild(g.getLocationURI().toASCIIString(), null);
return ProjectBuild.createGradleBuild(g.getLocationURI().toASCIIString());
}
}
// } catch (JavaModelException e) {
// // ignore
// }
return new ProjectBuild(null, null, null);
return new ProjectBuild(null, null);
}
}

View File

@@ -0,0 +1,150 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.tooling.jdt.ls.commons.java;
import java.io.File;
import java.net.URI;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import org.apache.maven.project.MavenProject;
import org.eclipse.buildship.core.GradleCore;
import org.eclipse.buildship.core.internal.configuration.GradleProjectNature;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.embedder.ArtifactKey;
import org.eclipse.m2e.core.project.IMavenProjectFacade;
import org.springframework.ide.vscode.commons.protocol.java.Gav;
import org.springframework.ide.vscode.commons.protocol.java.ProjectGavParams;
import org.springframework.tooling.gradle.StsGradleToolingModelBuilder;
import org.springframework.tooling.jdt.ls.commons.Logger;
import org.springframework.tooling.jdt.ls.commons.resources.ResourceUtils;
@SuppressWarnings("restriction")
public class BuildInfo {
public static CompletableFuture<List<Gav>> projectGAV(ProjectGavParams params, Executor executor, Logger logger) {
List<Gav> gavs = new ArrayList<>(params.projectUris().size());
for (int i = 0; i < params.projectUris().size(); i++) {
// Important to init with nulls all of them such that only successful futures would update values at corresponding indices
gavs.add(null);
}
List<CompletableFuture<?>> f = new ArrayList<>(params.projectUris().size());
for (int i = 0; i < params.projectUris().size(); i++) {
final int index = i;
try {
IProject project = ResourceUtils.getProject(URI.create(params.projectUris().get(index)));
if (project != null) {
f.add(BuildInfo.computeProjectGav(project, executor, logger).thenAccept(gav -> gavs.set(index, gav)));
}
} catch (Exception e) {
// ignore - all pre-filled with 'null' values
}
}
return CompletableFuture.allOf(f.toArray(new CompletableFuture<?>[f.size()])).thenApply(v -> gavs);
}
private static CompletableFuture<Gav> computeProjectGav(IProject project, Executor executor, Logger logger) {
try {
boolean likelyGradle = false;
boolean likelyMaven = false;
final Path home = System.getProperty("user.home") == null ? null : new File(System.getProperty("user.home")).toPath();
if (project.isAccessible() && project.exists()) {
if (MavenPlugin.isMavenProject(project)) {
IMavenProjectFacade facade = MavenPlugin.getMavenProjectRegistry().getProject(project);
if (facade != null) {
ArtifactKey artifact = facade.getArtifactKey();
return CompletableFuture.completedFuture(new Gav(artifact.groupId(), artifact.artifactId(), artifact.version()));
} else {
return CompletableFuture.supplyAsync(() -> getMavenGav(project.getFile("pom.xml").getFullPath().toFile(), logger), executor) ;
}
} else if (GradleProjectNature.isPresentOn(project)) {
return CompletableFuture.supplyAsync(() -> getGradleBuild(project, logger), executor);
} else {
IJavaProject jp = JavaCore.create(project);
if (jp != null) {
try {
for (IClasspathEntry e : jp.getRawClasspath()) {
if (home != null && e.getPath() != null && e.getPath().toFile() != null) {
Path path = e.getPath().toFile().toPath();
if (path.startsWith(home.resolve(".gradle"))) {
likelyGradle = true;
} else if (path.startsWith(home.resolve(".m2"))) {
likelyMaven = true;
}
}
}
} catch (Exception e) {
// ignore
}
}
}
}
if (likelyMaven) {
return CompletableFuture.supplyAsync(() -> getMavenGav(project.getFile("pom.xml").getFullPath().toFile(), logger), executor);
} else if (likelyGradle) {
return CompletableFuture.supplyAsync(() -> getGradleBuild(project, logger), executor);
}
} catch (Exception e) {
logger.log(e);
}
return CompletableFuture.completedFuture(null);
}
private static Gav getMavenGav(File pom, Logger logger) {
if (pom.exists()) {
long start = System.currentTimeMillis();
try {
MavenProject mavenProject = MavenPlugin.getMaven().readProject(pom, new NullProgressMonitor());
return new Gav(mavenProject.getGroupId(), mavenProject.getArtifactId(), mavenProject.getVersion());
} catch (CoreException e) {
logger.log(e);
} finally {
logger.log("Maven GAV project '%s' took %d".formatted(pom.getParentFile().getName(), System.currentTimeMillis() - start));
}
} else {
logger.log("%s does not exists!".formatted(pom.toString()));
}
return null;
}
private static Gav getGradleBuild(IProject project, Logger logger) {
IFile g = project.getFile("build.gradle");
if (!g.exists()) {
g = project.getFile("build.gradle.kts");
}
long start = System.currentTimeMillis();
try {
return GradleCore.getWorkspace().getBuild(project).flatMap(build -> {
try {
return Optional.of(build.withConnection(conn -> StsGradleToolingModelBuilder.getModelBuilder(conn, project.getLocation().toFile(), null).get(), new NullProgressMonitor()));
} catch (Exception e) {
logger.log(e);
return Optional.empty();
}
}).map(m -> new Gav(m.group(), m.artifact(), m.version())).orElse(null);
} finally {
logger.log("Gradle GAV project '%s' took %d".formatted(project.getName(), System.currentTimeMillis() - start));
}
}
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018, 2019 Pivotal, Inc.
* Copyright (c) 2018, 2024 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -34,8 +34,21 @@ public final class ResourceUtils {
* @throws Exception if unable to resolve Java project from given resource URI
*/
public static IJavaProject getJavaProject(URI resourceUri) throws Exception {
IProject project = getProject(resourceUri);
if (project.isAccessible() && project.hasNature(JavaCore.NATURE_ID)) {
return JavaCore.create(project);
}
throw new Exception("Resolve classpath: unable to resolve a Java project from : " + resourceUri);
}
/**
* Resolves a Java project given the resource URI. The resource URI could be any resource contained in the project.
* @param resourceUri
* @return Java project
* @throws Exception if unable to resolve Java project from given resource URI
*/
public static IProject getProject(URI resourceUri) throws Exception {
IContainer[] containers = ResourcesPlugin.getWorkspace().getRoot().findContainersForLocationURI(resourceUri);
// log("containers=" + containers);
if (containers.length > 0) {
// log("containers.length=" + containers.length);
Optional<IContainer> shortest = Arrays.stream(containers)
@@ -45,19 +58,12 @@ public final class ResourceUtils {
if (shortest.isPresent()) {
// log("shortest.fullpath=" + shortest.get().getFullPath());
IProject project = shortest.get().getProject();
return shortest.get().getProject();
// log("project=" + project.getName());
if (project.isAccessible() && project.hasNature(JavaCore.NATURE_ID)) {
IJavaProject javaProject = JavaCore.create(project);
// log("javaProject=" + javaProject.getElementName());
return javaProject;
}
}
}
throw new Exception("Resolve classpath: unable to resolve a Java project from : " + resourceUri);
throw new Exception("Resolve classpath: unable to resolve a project from : " + resourceUri);
}
public static Collection<IJavaProject> allJavaProjects() {

View File

@@ -63,6 +63,12 @@
id="sts.java.code.completions">
</command>
</delegateCommandHandler>
<delegateCommandHandler
class="org.springframework.tooling.jdt.ls.extension.ProjectGavHandler">
<command
id="sts.project.gav">
</command>
</delegateCommandHandler>
</extension>
</plugin>

View File

@@ -0,0 +1,42 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.tooling.jdt.ls.extension;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler;
import org.springframework.ide.vscode.commons.protocol.java.ProjectGavParams;
import org.springframework.tooling.jdt.ls.commons.Logger;
import org.springframework.tooling.jdt.ls.commons.java.BuildInfo;
import com.google.gson.Gson;
public class ProjectGavHandler implements IDelegateCommandHandler {
private Gson gson = new Gson();
@Override
public Object executeCommand(String commandId, List<Object> arguments, IProgressMonitor monitor) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(2);
JdtLsExtensionPlugin.getInstance().getLog().error("Recieved uris: " + arguments.get(0));
try {
ProjectGavParams params = gson.fromJson(gson.toJson(arguments.get(0)), ProjectGavParams.class);
JdtLsExtensionPlugin.getInstance().getLog().error("Params project number: " + params.projectUris().size());
return BuildInfo.projectGAV(params, executor, Logger.forEclipsePlugin(() -> JdtLsExtensionPlugin.getInstance())).get();
} finally {
executor.shutdown();
}
}
}

View File

@@ -23,12 +23,13 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.commons.java.IGav;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.protocol.java.Classpath;
import org.springframework.ide.vscode.commons.protocol.java.Gav;
import org.springframework.ide.vscode.commons.protocol.java.ProjectGavParams;
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
import org.springframework.ide.vscode.commons.protocol.spring.BeansParams;
@@ -42,8 +43,10 @@ public class WorkspaceBootExecutableProjects {
final private JavaProjectFinder projectFinder;
final private SpringSymbolIndex symbolIndex;
final private SimpleLanguageServer server;
public WorkspaceBootExecutableProjects(SimpleLanguageServer server, JavaProjectFinder projectFinder, SpringSymbolIndex symbolIndex) {
this.server = server;
this.projectFinder = projectFinder;
this.symbolIndex = symbolIndex;
server.onCommand(CMD, params -> findExecutableProjects());
@@ -64,9 +67,7 @@ public class WorkspaceBootExecutableProjects {
.filter(cpe -> !cpe.isTest() && !cpe.isSystem())
.map(cpe -> Classpath.isSource(cpe) ? cpe.getOutputFolder() : cpe.getPath())
.collect(Collectors.toSet());
IGav gav = project.getProjectBuild().getGav();
String gavStr = "%s:%s:%s".formatted(gav.getGroupId(), gav.getArtifactId(), gav.getVersion());
return Optional.of(new ExecutableProject(project.getElementName(), project.getLocationUri().toASCIIString(), gavStr, appBean.getType(), classpath));
return Optional.of(new ExecutableProject(project.getElementName(), project.getLocationUri().toASCIIString(), null, appBean.getType(), classpath));
} catch (Exception e) {
log.error("", e);
}
@@ -77,13 +78,28 @@ public class WorkspaceBootExecutableProjects {
private CompletableFuture<List<ExecutableProject>> findExecutableProjects() {
List<CompletableFuture<Optional<ExecutableProject>>> futures = projectFinder.all().stream()
.filter(p -> p.getProjectBuild().getGav() != null)
.filter(p -> SpringProjectUtil.isBootProject(p))
.map(this::mapToExecProject)
.collect(Collectors.toList());
List<ExecutableProject> executableProjects = Collections.synchronizedList(new ArrayList<>());
futures.forEach(f -> f.thenAccept(opt -> opt.ifPresent(executableProjects::add)));
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(v -> executableProjects);
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenCompose(v -> {
final long start = System.currentTimeMillis();
return server.getClient().projectGAV(new ProjectGavParams(executableProjects.stream().map(p -> p.uri()).toList())).thenApply(gavs -> {
List<ExecutableProject> filteredExecProjects = new ArrayList<>(executableProjects.size());
for (int i = 0; i < executableProjects.size(); i++) {
ExecutableProject ep = executableProjects.get(i);
if (gavs.get(i) != null) {
Gav gav = gavs.get(i);
filteredExecProjects.add(new ExecutableProject(ep.name(), ep.uri(), "%s:%s:%s".formatted(gav.groupId(), gav.artifactId(), gav.version()), ep.mainClass(), ep.classpath()));
} else {
filteredExecProjects.add(ep);
}
}
log.info("GAV for %d projects took: %d".formatted(executableProjects.size(), System.currentTimeMillis() - start));
return filteredExecProjects;
});
});
}
}

View File

@@ -385,7 +385,7 @@ public class JdtLsProjectCache implements InitializableJavaProjectsService, Serv
}
private static IProjectBuild from(ProjectBuild projectBuild) {
return projectBuild == null ? null : IProjectBuild.create(projectBuild.type(), projectBuild.buildFile() == null ? null : URI.create(projectBuild.buildFile()), projectBuild.gav());
return projectBuild == null ? null : IProjectBuild.create(projectBuild.type(), projectBuild.buildFile() == null ? null : URI.create(projectBuild.buildFile()));
}
}

View File

@@ -50,6 +50,7 @@ public class WorkspaceBootExecutableProjectsTest {
@BeforeEach
public void setup() throws Exception {
harness.intialize(null);
harness.setGavSupplier(ProjectsHarness.GAV_SUPPLIER);
}
@SuppressWarnings("unchecked")

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016, 2022 Pivotal, Inc.
* Copyright (c) 2016, 2024 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -17,6 +17,7 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Function;
import org.apache.commons.io.FileUtils;
import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath;
@@ -26,6 +27,7 @@ import org.springframework.ide.vscode.commons.javadoc.JavaDocProviders;
import org.springframework.ide.vscode.commons.maven.MavenBuilder;
import org.springframework.ide.vscode.commons.maven.MavenCore;
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
import org.springframework.ide.vscode.commons.protocol.java.Gav;
import org.springframework.ide.vscode.commons.util.BasicFileObserver;
import org.springframework.ide.vscode.commons.util.FileObserver;
import org.springframework.ide.vscode.commons.util.IOUtil;
@@ -50,6 +52,18 @@ public class ProjectsHarness {
public Cache<Object, IJavaProject> cache = CacheBuilder.newBuilder().concurrencyLevel(1).build();
private final FileObserver fileObserver;
public static final Function<File, Gav> GAV_SUPPLIER = f -> {
if ("pom.xml".equals(f.getName())) {
return MavenCore.getDefault().computeGav(f);
} else if (f.isDirectory()) {
File pom = new File(f, "pom.xml");
if (pom.exists()) {
return MavenCore.getDefault().computeGav(pom);
}
}
return null;
};
/**
* A callback that is given a chance to make changes to test project contents before the test project

File diff suppressed because it is too large Load Diff

View File

@@ -48,6 +48,11 @@ export function registerJavaDataService(client : LanguageClient) : void {
client.onRequest(javaCodeCompletion, async (params: JavaCodeCompleteParams) =>
<any> await VSCode.commands.executeCommand("java.execute.workspaceCommand", "sts.java.code.completions", params)
);
const projectGav = new RequestType<ProjectGavParams, any, void>("sts/project/gav");
client.onRequest(projectGav, async (params: ProjectGavParams) =>
<any> await VSCode.commands.executeCommand("java.execute.workspaceCommand", "sts.project.gav", params)
);
}
interface JavaDataParams {
@@ -77,3 +82,7 @@ interface JavaCodeCompleteParams {
includeTypes: boolean;
includePackages: boolean;
}
interface ProjectGavParams {
projectUris: string[];
}

View File

@@ -28,7 +28,7 @@ class TestJarDebugConfigProvider implements DebugConfigurationProvider {
}
const projectsWithErrors: ExecutableBootProject[] = [];
// Create all project classparth data files and add env vars for workspace projects
await Promise.all(projects.map(async p => {
await Promise.all(projects.filter(p => p.gav).map(async p => {
const envName = this.createEnvVarName(p);
if (!env[envName]) {
try {

View File

@@ -33,7 +33,8 @@
"./jars/io.projectreactor.reactor-core.jar",
"./jars/org.reactivestreams.reactive-streams.jar",
"./jars/jdt-ls-commons.jar",
"./jars/jdt-ls-extension.jar"
"./jars/jdt-ls-extension.jar",
"./jars/sts-gradle-tooling.jar"
],
"languages": [
{

View File

@@ -29,6 +29,7 @@ cd ${workdir}/../../headless-services/jdt-ls-extension
find . -name "*-sources.jar" -delete
cp org.springframework.tooling.jdt.ls.extension/target/*.jar ${workdir}/jars/jdt-ls-extension.jar
cp org.springframework.tooling.jdt.ls.commons/target/*.jar ${workdir}/jars/jdt-ls-commons.jar
cp org.springframework.tooling.gradle/target/*.jar ${workdir}/jars/sts-gradle-tooling.jar
# Copy Reactor dependency bundles
cp org.springframework.tooling.jdt.ls.commons/target/dependencies/io.projectreactor.reactor-core.jar ${workdir}/jars/