IDE support for TestJars: maven projects in the workspace only
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
/*******************************************************************************
|
||||
* 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.boot.app;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.ide.vscode.boot.java.commands.WorkspaceBootExecutableProjects;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class CommandsConfig {
|
||||
|
||||
@Bean WorkspaceBootExecutableProjects workspaceBootProjects(SimpleLanguageServer server, JavaProjectFinder projectFinder, SpringSymbolIndex symbolIndex) {
|
||||
return new WorkspaceBootExecutableProjects(server, projectFinder, symbolIndex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*******************************************************************************
|
||||
* 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.boot.java.commands;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.BeansParams;
|
||||
|
||||
public class WorkspaceBootExecutableProjects {
|
||||
|
||||
public record ExecutableProject(String name, String uri, String gav, String mainClass, Collection<String> classpath) {}
|
||||
|
||||
final static String CMD = "sts/spring-boot/executableBootProjects";
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(WorkspaceBootExecutableProjects.class);
|
||||
|
||||
final private JavaProjectFinder projectFinder;
|
||||
final private SpringSymbolIndex symbolIndex;
|
||||
|
||||
public WorkspaceBootExecutableProjects(SimpleLanguageServer server, JavaProjectFinder projectFinder, SpringSymbolIndex symbolIndex) {
|
||||
this.projectFinder = projectFinder;
|
||||
this.symbolIndex = symbolIndex;
|
||||
server.onCommand(CMD, params -> findExecutableProjects());
|
||||
}
|
||||
|
||||
private CompletableFuture<Optional<ExecutableProject>> mapToExecProject(IJavaProject project) {
|
||||
BeansParams params = new BeansParams();
|
||||
params.setProjectName(project.getElementName());
|
||||
return symbolIndex.beans(params).thenApply(beans -> {
|
||||
List<Bean> bootAppBeans = beans.stream()
|
||||
.filter(b -> Arrays.asList(b.getAnnotations()).contains(Annotations.BOOT_APP))
|
||||
.limit(2)
|
||||
.collect(Collectors.toList());
|
||||
if (bootAppBeans.size() == 1) {
|
||||
try {
|
||||
Bean appBean = bootAppBeans.get(0);
|
||||
Collection<String> classpath = project.getClasspath().getClasspathEntries().stream()
|
||||
.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));
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018, 2023 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
|
||||
@@ -385,7 +385,7 @@ public class JdtLsProjectCache implements InitializableJavaProjectsService, Serv
|
||||
}
|
||||
|
||||
private static IProjectBuild from(ProjectBuild projectBuild) {
|
||||
return projectBuild == null ? null : IProjectBuild.create(projectBuild.getType(), projectBuild.getBuildFile() == null ? null : URI.create(projectBuild.getBuildFile()));
|
||||
return projectBuild == null ? null : IProjectBuild.create(projectBuild.type(), projectBuild.buildFile() == null ? null : URI.create(projectBuild.buildFile()), projectBuild.gav());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*******************************************************************************
|
||||
* 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.boot.java.commands;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.lsp4j.ExecuteCommandParams;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
|
||||
import org.springframework.ide.vscode.boot.java.commands.WorkspaceBootExecutableProjects.ExecutableProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@BootLanguageServerTest
|
||||
@Import(SymbolProviderTestConf.class)
|
||||
public class WorkspaceBootExecutableProjectsTest {
|
||||
|
||||
@Autowired private BootLanguageServerHarness harness;
|
||||
@Autowired private JavaProjectFinder projectFinder;
|
||||
@Autowired private SpringSymbolIndex symbolIndex;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
harness.intialize(null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
void singleProject() throws Exception {
|
||||
MavenJavaProject project = ProjectsHarness.INSTANCE.mavenProject("test-spring-indexing");
|
||||
|
||||
// trigger project creation
|
||||
projectFinder.find(new TextDocumentIdentifier(project.getProjectBuild().getBuildFile().toASCIIString())).get();
|
||||
|
||||
CompletableFuture<Void> initProject = symbolIndex.waitOperation();
|
||||
initProject.get(5, TimeUnit.SECONDS);
|
||||
|
||||
List<WorkspaceBootExecutableProjects.ExecutableProject> res = (List<WorkspaceBootExecutableProjects.ExecutableProject>) harness.getServer().getWorkspaceService().executeCommand(new ExecuteCommandParams(WorkspaceBootExecutableProjects.CMD, Collections.emptyList())).get();
|
||||
assertNotNull(res);
|
||||
assertEquals(1, res.size());
|
||||
ExecutableProject execProject = res.get(0);
|
||||
assertEquals("test-spring-indexing", execProject.name());
|
||||
assertEquals("org.test.MainClass", execProject.mainClass());
|
||||
assertEquals(project.getLocationUri().toASCIIString(), execProject.uri());
|
||||
assertEquals("com.example:test-spring-indexing:0.0.1-SNAPSHOT", execProject.gav());
|
||||
assertEquals(99, execProject.classpath().size());
|
||||
|
||||
assertTrue(execProject.classpath().stream().map(path -> Path.of(path)).anyMatch(p -> p.endsWith("target/classes")));
|
||||
assertFalse(execProject.classpath().stream().map(path -> Path.of(path)).anyMatch(p -> p.endsWith("target/test-classes")));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
void multipleProjects() throws Exception {
|
||||
MavenJavaProject project1 = ProjectsHarness.INSTANCE.mavenProject("test-spring-indexing");
|
||||
MavenJavaProject project2 = ProjectsHarness.INSTANCE.mavenProject("test-spring-data-symbols");
|
||||
ProjectsHarness.INSTANCE.mavenProject("test-annotation-indexing-non-boot-project");
|
||||
ProjectsHarness.INSTANCE.mavenProject("test-xml-validations");
|
||||
|
||||
|
||||
// trigger project creation
|
||||
projectFinder.find(new TextDocumentIdentifier(project1.getProjectBuild().getBuildFile().toASCIIString())).get();
|
||||
projectFinder.find(new TextDocumentIdentifier(project2.getProjectBuild().getBuildFile().toASCIIString())).get();
|
||||
|
||||
CompletableFuture<Void> initProject = symbolIndex.waitOperation();
|
||||
initProject.get(10, TimeUnit.SECONDS);
|
||||
|
||||
List<WorkspaceBootExecutableProjects.ExecutableProject> res = (List<WorkspaceBootExecutableProjects.ExecutableProject>) harness.getServer().getWorkspaceService().executeCommand(new ExecuteCommandParams(WorkspaceBootExecutableProjects.CMD, Collections.emptyList())).get();
|
||||
assertNotNull(res);
|
||||
assertEquals(2, res.size());
|
||||
|
||||
ExecutableProject execProject2 = res.get(0);
|
||||
assertEquals("test-spring-data-symbols", execProject2.name());
|
||||
assertEquals("org.test.Application", execProject2.mainClass());
|
||||
assertEquals(project2.getLocationUri().toASCIIString(), execProject2.uri());
|
||||
assertEquals("com.example:test-spring-data-symbols:0.0.1-SNAPSHOT", execProject2.gav());
|
||||
assertEquals(44, execProject2.classpath().size());
|
||||
assertTrue(execProject2.classpath().stream().map(path -> Path.of(path)).anyMatch(p -> p.endsWith("target/classes")));
|
||||
assertFalse(execProject2.classpath().stream().map(path -> Path.of(path)).anyMatch(p -> p.endsWith("target/test-classes")));
|
||||
|
||||
ExecutableProject execProject1 = res.get(1);
|
||||
assertEquals("test-spring-indexing", execProject1.name());
|
||||
assertEquals("org.test.MainClass", execProject1.mainClass());
|
||||
assertEquals(project1.getLocationUri().toASCIIString(), execProject1.uri());
|
||||
assertEquals("com.example:test-spring-indexing:0.0.1-SNAPSHOT", execProject1.gav());
|
||||
assertEquals(99, execProject1.classpath().size());
|
||||
assertTrue(execProject1.classpath().stream().map(path -> Path.of(path)).anyMatch(p -> p.endsWith("target/classes")));
|
||||
assertFalse(execProject1.classpath().stream().map(path -> Path.of(path)).anyMatch(p -> p.endsWith("target/test-classes")));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user