From 087aa62de815945159927ed83eb9a6b08371b84d Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Tue, 28 Oct 2025 16:18:54 -0700 Subject: [PATCH 1/2] Pass Java installation and info from Java LS per project Signed-off-by: BoykoAlex --- .../gradle/GradleProjectClasspath.java | 18 ++++--- .../vscode/commons/java/ClasspathData.java | 17 ++++--- .../java/DelegatingCachedClasspath.java | 5 +- .../ide/vscode/commons/java/IClasspath.java | 7 +-- .../ide/vscode/commons/java/JavaUtils.java | 51 +++++++++++++++++-- .../commons/jandex/JandexClasspathTest.java | 2 +- .../commons/protocol/java/Classpath.java | 19 ++++--- .../ide/vscode/commons/protocol/java/VM.java | 14 +++++ .../ide/vscode/commons/maven/MavenCore.java | 12 +++-- .../maven/java/MavenProjectClasspath.java | 9 ++-- .../META-INF/MANIFEST.MF | 3 +- .../ls/commons/classpath/ClasspathUtil.java | 41 ++++++--------- .../SendClasspathNotificationsJob.java | 2 +- .../WorkspaceBootExecutableProjects.java | 2 +- .../boot/java/utils/SpringIndexerJava.java | 22 +++++++- .../vscode/boot/jdt/ls/JdtLsProjectCache.java | 2 +- .../vscode/boot/mcp/ProjectInformation.java | 4 +- .../vscode/boot/modulith/ModulithService.java | 8 ++- .../boot/java/value/test/MockProjects.java | 10 ++-- .../lib/copilot/springBootAgent.ts | 9 +++- 20 files changed, 173 insertions(+), 84 deletions(-) create mode 100644 headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/VM.java diff --git a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java index 66b256ffc3..df498e68cd 100644 --- a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java +++ b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java @@ -15,6 +15,8 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; import java.util.stream.Stream; import org.gradle.tooling.model.build.BuildEnvironment; @@ -25,6 +27,7 @@ import org.springframework.ide.vscode.commons.java.JavaUtils; 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.VM; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; @@ -128,8 +131,11 @@ public String getName() { } @Override - public String getJavaVersion() { - return JavaUtils.getJavaRuntimeMinorVersion(getJavaRuntimeVersion()); + public VM getVM() { + if (buildEnvironment == null) { + throw new IllegalArgumentException("No Gradle build available"); + } + return new VM(getJavaRuntimeVersion(), buildEnvironment.getJava().getJavaHome().toPath().toString()); } public String getGradleVersion() throws GradleException { @@ -152,14 +158,14 @@ public String getJavaRuntimeVersion() { return System.getProperty(JAVA_RUNTIME_VERSION); } - private String getJavaHome() { + public Optional getJavaHome() { if (buildEnvironment == null) { - return System.getProperty(JAVA_HOME); + return Optional.of(Paths.get(System.getProperty(JAVA_HOME))); } else { - return buildEnvironment.getJava().getJavaHome().toString(); + return Optional.of(buildEnvironment.getJava().getJavaHome().toPath()); } } - + private Stream getJreLibs() { return JavaUtils.jreLibs(() -> JavaUtils.getJavaRuntimeMinorVersion(getJavaRuntimeVersion()), this::getJavaHome, () -> System.getProperty(JAVA_BOOT_CLASS_PATH)); } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathData.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathData.java index b801c81d33..0e0b1983cd 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathData.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathData.java @@ -19,6 +19,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.protocol.java.Classpath.CPE; +import org.springframework.ide.vscode.commons.protocol.java.VM; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; @@ -36,16 +37,16 @@ public class ClasspathData implements IClasspath { private String name; private Set classpathEntries; - private String javaVersion; + private VM vm; private Cache> binaryLibLookupCache; public ClasspathData() {} - public ClasspathData(String name, Collection classpathEntries, String javaVersion) { + public ClasspathData(String name, Collection classpathEntries, VM vm) { this.name = name; this.classpathEntries = ImmutableSet.copyOf(classpathEntries); - this.javaVersion = javaVersion; + this.vm = vm; this.binaryLibLookupCache = CacheBuilder.newBuilder().build(); } @@ -60,7 +61,7 @@ public static ClasspathData from(IClasspath d) { return new ClasspathData( d.getName(), entries==null ? ImmutableSet.of() : entries, - d.getJavaVersion() + d.getVM() ); } @@ -74,12 +75,12 @@ public void setName(String name) { } @Override - public String getJavaVersion() { - return javaVersion; + public VM getVM() { + return vm; } - public void setJavaVersion(String javaVersion) { - this.javaVersion = javaVersion; + public void setVM(VM javaVersion) { + this.vm = vm; } @Override diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java index 83fa5ee435..581e2baac8 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java @@ -14,6 +14,7 @@ import java.util.concurrent.atomic.AtomicReference; import org.springframework.ide.vscode.commons.protocol.java.Classpath.CPE; +import org.springframework.ide.vscode.commons.protocol.java.VM; import org.springframework.ide.vscode.commons.util.Assert; import com.google.common.base.Objects; @@ -81,8 +82,8 @@ public ImmutableList getClasspathEntries() throws Exception { } @Override - public String getJavaVersion() { - return cachedData.get().getJavaVersion(); + public VM getVM() { + return cachedData.get().getVM(); } public boolean isCached() { diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java index 422db8163f..c18cf562c3 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2023 Pivotal, Inc. + * Copyright (c) 2016, 2025 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 @@ -18,6 +18,7 @@ import org.slf4j.LoggerFactory; 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.VM; /** * Classpath for a Java artifact @@ -59,9 +60,9 @@ default Optional findBinaryLibrary(String prefix) { } /** - * Finds Java Version by parsing the classpath entries + * VM info * @return returns java version */ - String getJavaVersion(); + VM getVM(); } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JavaUtils.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JavaUtils.java index 70b2ae590e..53f4508e8d 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JavaUtils.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JavaUtils.java @@ -15,6 +15,8 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; +import java.util.List; +import java.util.Optional; import java.util.stream.Stream; import org.slf4j.Logger; @@ -31,6 +33,45 @@ public class JavaUtils { private static Logger log = LoggerFactory.getLogger(JavaUtils.class); + + private static final String JRE = "jre"; //$NON-NLS-1$ + + /** + * The list of locations in which to look for the java executable in candidate + * VM install locations, relative to the VM install location. From Java 9 onwards, there may not be a jre directory. + */ + private static final List CANDIDATE_JAVA_FILES = Stream.of("javaw", "javaw.exe", "java", "java.exe", "j9w", "j9w.exe", "j9", "j9.exe").map(Path::of).toList(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + private static final Path[] CANDIDATE_JAVA_LOCATIONS = { Path.of(""), Path.of("bin"), Path.of(JRE, "bin") }; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + private static final Path BIN = Path.of("bin"); //$NON-NLS-1$ + + /** + * Starting in the specified VM install location, attempt to find the 'java' executable + * file. If found, return the corresponding File object, otherwise return + * null. + * @param vmInstallLocation the {@link File} location to look in + * @return the {@link File} for the Java executable or null + */ + public static File findJavaExecutable(File vmInstallLocation) { + // Try each candidate in order. The first one found wins. Thus, the order + // of fgCandidateJavaLocations and fgCandidateJavaFiles is significant. + + Path filePath = vmInstallLocation.toPath(); + boolean isBin = filePath.endsWith(BIN); + for (Path exeName : CANDIDATE_JAVA_FILES) { + for (int j = 0; j < CANDIDATE_JAVA_LOCATIONS.length; j++) { + if (!isBin && j == 0) { + // search in "." only under bin for java executables for Java 9 and above + continue; + } + Path javaFile = filePath.resolve(CANDIDATE_JAVA_LOCATIONS[j]).resolve(exeName); + if (Files.isRegularFile(javaFile)) { + return javaFile.toFile(); + } + } + } + return null; + } + /** * Find JRE libs jars @@ -40,14 +81,14 @@ public class JavaUtils { * @param bootClasspathSupplier * @return */ - public static Stream jreLibs(Supplier javaMinorVersionSupplier, Supplier javaHomeSupplier, Supplier bootClasspathSupplier) { + public static Stream jreLibs(Supplier javaMinorVersionSupplier, Supplier> javaHomeSupplier, Supplier bootClasspathSupplier) { String versionString = javaMinorVersionSupplier.get(); try { int version = versionString == null ? 8 : Integer.valueOf(versionString); if (version > 8) { - String javaHome = javaHomeSupplier.get(); - if (javaHome != null) { - Path rtPath= Paths.get(javaHome, "lib", "jrt-fs.jar"); + Optional javaHomeOpt = javaHomeSupplier.get(); + if (javaHomeOpt.isPresent()) { + Path rtPath= javaHomeOpt.get().resolve("lib").resolve("jrt-fs.jar"); if (Files.exists(rtPath)) { return Stream.of(rtPath); } else { @@ -74,7 +115,7 @@ public static Stream jreLibs(Supplier javaMinorVersionSupplier, Su */ public static String getJavaRuntimeMinorVersion(String fullVersion) { String[] tokenized = fullVersion.split("\\."); - if (tokenized[0] == "1") { + if ("1".equals(tokenized[0])) { if (tokenized.length > 1) { return tokenized[1]; } else { diff --git a/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/jandex/JandexClasspathTest.java b/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/jandex/JandexClasspathTest.java index 0f25cbfb1b..c13ad6dcdd 100644 --- a/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/jandex/JandexClasspathTest.java +++ b/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/jandex/JandexClasspathTest.java @@ -63,7 +63,7 @@ void createClass(String fqName) throws Exception { ClasspathData getClasspath() { return new ClasspathData(name, ImmutableList.of( CPE.source(new File(root, "src"), outputFolder) - ), ""); + ), null); } JandexClasspath getJandexClasspath() { diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/Classpath.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/Classpath.java index cb71b6c592..4bd196c7e6 100644 --- a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/Classpath.java +++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/Classpath.java @@ -28,15 +28,14 @@ public class Classpath { public static final String ENTRY_KIND_SOURCE = "source"; public static final String ENTRY_KIND_BINARY = "binary"; - public static final Classpath EMPTY = new Classpath(Collections.emptyList(), ""); + public static final Classpath EMPTY = new Classpath(Collections.emptyList(), null); private List entries; - private String javaVersion; + private VM vm; - public Classpath(List entries, String javaVersion) { - super(); + public Classpath(List entries, VM vm) { this.entries = entries; - this.javaVersion = javaVersion; + this.vm = vm; } public List getEntries() { @@ -47,17 +46,17 @@ public void setEntries(List entries) { this.entries = entries; } - public String getJavaVersion() { - return javaVersion; + public VM getVM() { + return vm; } - public void setJavaVersion(String javaVersion) { - this.javaVersion = javaVersion; + public void setVM(VM vm) { + this.vm = vm; } @Override public String toString() { - return "Classpath [entries=" + entries + ", javaVersion=" + javaVersion + "]"; + return "Classpath [entries=" + entries + ", vm=" + vm + "]"; } public static class CPE { diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/VM.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/VM.java new file mode 100644 index 0000000000..52b3bfaa9a --- /dev/null +++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/VM.java @@ -0,0 +1,14 @@ +/******************************************************************************* + * Copyright (c) 2025 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; + +public record VM(String version, String installationPath) { +} diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java index c0a90b3fbb..93e9e82e8c 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java @@ -17,6 +17,7 @@ import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -24,6 +25,7 @@ import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -272,10 +274,10 @@ public Artifact getTestSources(Artifact artifact, List repos public Stream getJreLibs() throws MavenException { return JavaUtils.jreLibs(this::getJavaRuntimeMinorVersion, () -> { try { - return maven.createExecutionRequest().getSystemProperties().getProperty(JAVA_HOME); + return Optional.of(Paths.get(getJavaHome())); } catch (MavenException e) { log.error("Cannot determine java home", e); - return null; + return Optional.empty(); } }, () -> { @@ -292,7 +294,11 @@ public Stream getJreLibs() throws MavenException { public String getJavaRuntimeVersion() throws MavenException { return maven.createExecutionRequest().getSystemProperties().getProperty(JAVA_RUNTIME_VERSION); } - + + public String getJavaHome() throws MavenException { + return maven.createExecutionRequest().getSystemProperties().getProperty(JAVA_HOME); + } + public String getJavaRuntimeMinorVersion() { try { return JavaUtils.getJavaRuntimeMinorVersion(getJavaRuntimeVersion()); diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java index 70b3cf7bf1..908843b005 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java @@ -13,6 +13,7 @@ import java.io.File; import java.net.URL; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; @@ -30,6 +31,7 @@ import org.springframework.ide.vscode.commons.maven.MavenCore; import org.springframework.ide.vscode.commons.maven.MavenException; import org.springframework.ide.vscode.commons.protocol.java.Classpath.CPE; +import org.springframework.ide.vscode.commons.protocol.java.VM; import org.springframework.ide.vscode.commons.util.RunnableWithException; import com.google.common.base.Objects; @@ -228,8 +230,8 @@ public ImmutableList getClasspathEntries() throws Exception { } @Override - public String getJavaVersion() { - return cachedData.getJavaVersion() != null ? cachedData.getJavaVersion() : null; + public VM getVM() { + return cachedData.getVM() != null ? cachedData.getVM() : null; } private Set projectDependencies(MavenProject project) { @@ -258,8 +260,9 @@ private ClasspathData createClasspathData() throws Exception { ImmutableList entries = resolveClasspathEntries(project); String name = project.getArtifact().getArtifactId(); String javaVersion = maven.getJavaRuntimeVersion(); + String javaHome = maven.getJavaHome(); - return new ClasspathData(name, new LinkedHashSet<>(entries), javaVersion); + return new ClasspathData(name, new LinkedHashSet<>(entries), new VM(javaVersion, Paths.get(javaHome).toString())); } @Override diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/META-INF/MANIFEST.MF b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/META-INF/MANIFEST.MF index e3bcf682f1..8ea9ea04a9 100644 --- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/META-INF/MANIFEST.MF +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/META-INF/MANIFEST.MF @@ -17,7 +17,8 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.m2e.core, org.eclipse.buildship.core, org.springframework.tooling.gradle, - org.eclipse.m2e.maven.runtime + org.eclipse.m2e.maven.runtime, + org.eclipse.jdt.launching Export-Package: org.springframework.ide.vscode.commons.protocol, org.springframework.ide.vscode.commons.protocol.java, org.springframework.ide.vscode.commons.protocol.spring, diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java index a95976fb26..03a2963a73 100644 --- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java @@ -37,10 +37,14 @@ import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.core.JavaProject; +import org.eclipse.jdt.launching.IVMInstall; +import org.eclipse.jdt.launching.IVMInstall2; +import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.m2e.core.MavenPlugin; 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.ProjectBuild; +import org.springframework.ide.vscode.commons.protocol.java.VM; import org.springframework.tooling.jdt.ls.commons.Logger; @SuppressWarnings("restriction") @@ -48,9 +52,8 @@ public class ClasspathUtil { private static final Object JRE_CONTAINER_ID = "org.eclipse.jdt.launching.JRE_CONTAINER"; - private static Set getSystemLibraryPaths(IJavaProject javaProject) { + private static Set getSystemLibraryPaths(IJavaProject javaProject, IClasspathEntry jreContainer) { try { - IClasspathEntry jreContainer = getJreContainer(javaProject.getRawClasspath()); if (jreContainer!=null) { IClasspathEntry[] resolvedJreEntries = ((JavaProject)javaProject).resolveClasspath(new IClasspathEntry[] {jreContainer}); Set paths = new HashSet<>(); @@ -83,7 +86,8 @@ public static Classpath resolve(IJavaProject javaProject, Logger logger) throws List cpEntries = new ArrayList<>(); IClasspathEntry[] entries = javaProject.getResolvedClasspath(true); - Set systemLibs = getSystemLibraryPaths(javaProject); + IClasspathEntry jreContainer = getJreContainer(javaProject.getRawClasspath()); + Set systemLibs = getSystemLibraryPaths(javaProject, jreContainer); if (entries != null) { for (IClasspathEntry entry : entries) { @@ -94,36 +98,21 @@ public static Classpath resolve(IJavaProject javaProject, Logger logger) throws } } } - IClasspathEntry jreContainer = getJreContainer(javaProject.getRawClasspath()); - String javaVersion = jreContainer == null ? null : extractJavaVersion(jreContainer.getPath().lastSegment()); - Classpath classpath = new Classpath(cpEntries, javaVersion); + IVMInstall vmInstall = JavaRuntime.getVMInstall(javaProject); + if (vmInstall == null) { + vmInstall = JavaRuntime.getVMInstall(jreContainer.getPath()); + } + String javaVersion = vmInstall instanceof IVMInstall2 ? ((IVMInstall2) vmInstall).getJavaVersion() : null; + String installationPath = vmInstall == null ? null : vmInstall.getInstallLocation().toPath().toString(); + Classpath classpath = new Classpath(cpEntries, vmInstall == null ? null : new VM(javaVersion, installationPath)); logger.debug("classpath=" + classpath.getEntries().size() + " entries"); return classpath; } - private static String extractJavaVersion(String versionString) { - String[] parts = versionString.split("-"); - if (parts.length > 1) { - return parts[1]; // for "JavaSE-17" style - } else if (versionString.contains(".")) { - parts = versionString.split("\\."); - if (parts[0] == "1") { - if (parts.length > 1) { - return parts[1]; - } - } else { - String version = parts[0]; - int idx = version.indexOf('+'); - return idx >= 0 ? version.substring(0, idx) : version; - } - } - return versionString; - } - private static AtomicBoolean enabledDownloadSources = new AtomicBoolean(false); public static List createCpes(IJavaProject javaProject, IClasspathEntry entry) throws MalformedURLException, JavaModelException { - return createCpes(getSystemLibraryPaths(javaProject), javaProject, entry); + return createCpes(getSystemLibraryPaths(javaProject, getJreContainer(javaProject.getRawClasspath())), javaProject, entry); } public static void enableDownloadSources() { diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/SendClasspathNotificationsJob.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/SendClasspathNotificationsJob.java index 85f309fb1e..3ab9f88d16 100644 --- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/SendClasspathNotificationsJob.java +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/SendClasspathNotificationsJob.java @@ -137,7 +137,7 @@ protected IStatus run(IProgressMonitor monitor) { } if (filteredCPEs.size() != classpath.getEntries().size()) { // Only send effective classpath that has all entries physically present. - classpath = new Classpath(filteredCPEs, ""); + classpath = new Classpath(filteredCPEs, null); } } catch (Exception e) { logger.log(e); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/commands/WorkspaceBootExecutableProjects.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/commands/WorkspaceBootExecutableProjects.java index db2192a6f2..a5ee1f61e0 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/commands/WorkspaceBootExecutableProjects.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/commands/WorkspaceBootExecutableProjects.java @@ -138,7 +138,7 @@ private CompletableFuture> mapToBootProjectInfo(IJavaP String appBean = bootAppBeans.get(0) != null ? bootAppBeans.get(0).getType() : null; String springBootVersion = SpringProjectUtil.getSpringBootVersion(project).toString(); String buildTool = project.getProjectBuild().getType(); - String javaVersion = project.getClasspath().getJavaVersion(); + String javaVersion = project.getClasspath().getVM() == null ? null : project.getClasspath().getVM().version(); return Optional .of(new BootProjectInfo(project.getElementName(), project.getLocationUri().toASCIIString(), appBean, buildTool, springBootVersion, javaVersion)); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java index 82f8910b95..650bf35c1e 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java @@ -1054,15 +1054,33 @@ private WorkspaceSymbol provideDefaultSymbol(Annotation node, final SpringIndexe public static ASTParserCleanupEnabled createParser(IJavaProject project, AnnotationHierarchies annotationHierarchies, boolean ignoreMethodBodies) throws Exception { String[] classpathEntries = getClasspathEntries(project); String[] sourceEntries = getSourceEntries(project); - String complianceJavaVersion = getComplianceJavaVersion(project.getClasspath().getJavaVersion()); + String complianceJavaVersion = getComplianceJavaVersion(project.getClasspath().getVM() == null ? null : project.getClasspath().getVM().version()); return new ASTParserCleanupEnabled(classpathEntries, sourceEntries, complianceJavaVersion, annotationHierarchies, ignoreMethodBodies); } private static String getComplianceJavaVersion(String javaVersion) { + String complianceLevel = extractComplianceVersion(javaVersion); // Currently the java version in the classpath seems to be 1.8, 17, 21 etc. - return javaVersion == null || javaVersion.isBlank() ? JavaCore.VERSION_21 : javaVersion; + return complianceLevel == null || complianceLevel.isBlank() ? JavaCore.VERSION_25 : complianceLevel; } + + private static String extractComplianceVersion(String versionString) { + if (versionString.contains(".")) { + String[] parts = versionString.split("\\."); + if ("1".equals(parts[0])) { + if (parts.length > 1) { + return "%s.%s".formatted(parts[0], parts[1]); + } + } else { + String version = parts[0]; + int idx = version.indexOf('+'); + return idx >= 0 ? version.substring(0, idx) : version; + } + } + return null; + } + private static String[] getClasspathEntries(IJavaProject project) throws Exception { IClasspath classpath = project.getClasspath(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java index 6e4c8d045a..0b647cad76 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java @@ -370,7 +370,7 @@ public synchronized void changed(Event event) { } else { log.debug("deleted = false"); URI projectUri = new URI(uri); - ClasspathData classpath = new ClasspathData(event.name, event.classpath.getEntries(), event.classpath.getJavaVersion()); + ClasspathData classpath = new ClasspathData(event.name, event.classpath.getEntries(), event.classpath.getVM()); IJavaProject oldProject, newProject; synchronized(table) { oldProject = table.get(uri); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/ProjectInformation.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/ProjectInformation.java index 9611a38b5d..bae3d8fc2f 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/ProjectInformation.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/ProjectInformation.java @@ -44,7 +44,7 @@ public ProjectInformation(JavaProjectFinder projectFinder) { public List getProjectList() throws Exception { return projectFinder.all() .stream() - .map(project -> new Project(project.getElementName(), SpringProjectUtil.isBootProject(project), project.getClasspath().getJavaVersion())) + .map(project -> new Project(project.getElementName(), SpringProjectUtil.isBootProject(project), project.getClasspath().getVM() == null ? null : project.getClasspath().getVM().version())) .toList(); } @@ -77,7 +77,7 @@ public String getJavaVersion( IJavaProject project = getProject(projectName); IClasspath classpath = project.getClasspath(); - return classpath.getJavaVersion(); + return classpath.getVM().version(); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/modulith/ModulithService.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/modulith/ModulithService.java index 49ddb10cb1..41be712c71 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/modulith/ModulithService.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/modulith/ModulithService.java @@ -46,12 +46,14 @@ import org.springframework.ide.vscode.boot.java.handlers.BootJavaReconcileEngine; import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.JavaUtils; import org.springframework.ide.vscode.commons.java.SpringProjectUtil; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory.Toggle.Option; 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.VM; import org.springframework.ide.vscode.commons.protocol.spring.BeansParams; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -323,7 +325,9 @@ private CompletableFuture loadModulesMetadata(IJavaProject project, return findRootPackages(project, delay).thenComposeAsync(packages -> { if (!packages.isEmpty()) { try { - String javaCmd = ProcessHandle.current().info().command().orElseThrow(); + VM vm = project.getClasspath().getVM(); + String exec = vm == null ? ProcessHandle.current().info().command().orElseThrow() + : JavaUtils.findJavaExecutable(Paths.get(vm.installationPath()).toFile()).toString(); String classpathStr = project.getClasspath().getClasspathEntries().stream().map(cpe -> { if (Classpath.ENTRY_KIND_SOURCE.equals(cpe.getKind())) { return cpe.getOutputFolder(); @@ -333,7 +337,7 @@ private CompletableFuture loadModulesMetadata(IJavaProject project, }).collect(Collectors.joining(System.getProperty("path.separator"))); List allAppModules = new ArrayList<>(); CompletableFuture[] aggregateFuture = packages.stream() - .map(pkg -> CompletableFuture.supplyAsync(() -> computeAppModules(project.getElementName(), javaCmd, classpathStr, pkg), executor) + .map(pkg -> CompletableFuture.supplyAsync(() -> computeAppModules(project.getElementName(), exec, classpathStr, pkg), executor) .thenAccept(allAppModules::addAll)) .toArray(CompletableFuture[]::new); return CompletableFuture.allOf(aggregateFuture).thenApply(r -> new AppModules(allAppModules)); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/MockProjects.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/MockProjects.java index 8e8f3e2b23..cca7e801f8 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/MockProjects.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/MockProjects.java @@ -10,6 +10,9 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.value.test; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; + import java.io.ByteArrayInputStream; import java.io.File; import java.net.URI; @@ -27,10 +30,6 @@ import java.util.function.Consumer; import org.eclipse.lsp4j.TextDocumentIdentifier; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; - import org.springframework.ide.vscode.commons.java.ClasspathIndex; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; @@ -40,6 +39,7 @@ import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver.Listener; 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.VM; import org.springframework.ide.vscode.commons.util.FileObserver; import org.springframework.ide.vscode.commons.util.IOUtil; @@ -103,7 +103,7 @@ public Collection getClasspathEntries() throws Exception { } @Override - public String getJavaVersion() { + public VM getVM() { // return javaVersion; return null; } diff --git a/vscode-extensions/vscode-spring-boot/lib/copilot/springBootAgent.ts b/vscode-extensions/vscode-spring-boot/lib/copilot/springBootAgent.ts index 279fa142ec..3d544a41cf 100644 --- a/vscode-extensions/vscode-spring-boot/lib/copilot/springBootAgent.ts +++ b/vscode-extensions/vscode-spring-boot/lib/copilot/springBootAgent.ts @@ -14,7 +14,12 @@ interface BootProjectInfo { mainClass: string; buildTool: string; springBootVersion: string; - javaVersion: string; + vm: VM; +} + +interface VM { + version: string; + installationPath: string; } interface SpringBootChatAgentResult extends ChatResult { @@ -49,7 +54,7 @@ export default class SpringBootChatAgent { Root Package name: ${bootProjInfo.mainClass.substring(0, bootProjInfo.mainClass.lastIndexOf('.'))} Build tool: ${bootProjInfo.buildTool} Spring Boot version: ${bootProjInfo.springBootVersion} - Java version: ${bootProjInfo.javaVersion} + Java version: ${bootProjInfo?.vm.version} User prompt: ${request.prompt} `; From 1f691f10905326224c6a7303ee6ea80aa5a016f2 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Wed, 29 Oct 2025 11:28:18 -0700 Subject: [PATCH 2/2] VM -> Jre Signed-off-by: BoykoAlex --- .../gradle/GradleProjectClasspath.java | 26 ++++--------------- .../vscode/commons/java/ClasspathData.java | 18 ++++++------- .../java/DelegatingCachedClasspath.java | 6 ++--- .../ide/vscode/commons/java/IClasspath.java | 4 +-- .../commons/protocol/java/Classpath.java | 16 ++++++------ .../protocol/java/{VM.java => Jre.java} | 2 +- .../maven/java/MavenProjectClasspath.java | 22 +++++----------- .../ls/commons/classpath/ClasspathUtil.java | 4 +-- .../WorkspaceBootExecutableProjects.java | 2 +- .../boot/java/utils/SpringIndexerJava.java | 2 +- .../vscode/boot/jdt/ls/JdtLsProjectCache.java | 2 +- .../vscode/boot/mcp/ProjectInformation.java | 4 +-- .../vscode/boot/modulith/ModulithService.java | 8 +++--- .../boot/java/value/test/MockProjects.java | 24 ++++++++++------- .../lib/copilot/springBootAgent.ts | 9 ++----- 15 files changed, 61 insertions(+), 88 deletions(-) rename headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/{VM.java => Jre.java} (90%) diff --git a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java index df498e68cd..20cc6134f6 100644 --- a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java +++ b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java @@ -12,7 +12,7 @@ import java.io.File; import java.net.MalformedURLException; -import java.net.URL; +import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -27,7 +27,7 @@ import org.springframework.ide.vscode.commons.java.JavaUtils; 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.VM; +import org.springframework.ide.vscode.commons.protocol.java.Jre; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; @@ -53,20 +53,8 @@ public GradleProjectClasspath(GradleCore gradle, File projectDir) throws GradleE this.buildEnvironment = gradle.getModel(projectDir, BuildEnvironment.class); } - private EclipseProject getRootProject() { - EclipseProject root = project; - if (root == null) { - return root; - } - while(root.getParent() != null) { - root = root.getParent(); - } - return root; - } - @Override public ImmutableList getClasspathEntries() throws Exception { - EclipseProject root = getRootProject(); if (project == null) { return ImmutableList.of(); } else { @@ -79,7 +67,7 @@ public ImmutableList getClasspathEntries() throws Exception { } String urlStr = "https://docs.oracle.com/javase/" + javaVersion + "/docs/api/"; try { - cpe.setJavadocContainerUrl(new URL(urlStr)); + cpe.setJavadocContainerUrl(URI.create(urlStr).toURL()); } catch (MalformedURLException e) { log.error("Invalid javadoc URL: " + urlStr, e); } @@ -121,21 +109,17 @@ private static CPE createSourceCPE(EclipseProject project, EclipseSourceDirector return CPE.source(sourceFolder.getAbsoluteFile(), new File(project.getProjectDirectory(), of)); } - private EclipseProject findPeer(EclipseProject root, String name) { - return root.getChildren().stream().filter(p -> p.getName().equals(name)).findFirst().orElse(null); - } - @Override public String getName() { return project == null ? null : project.getName(); } @Override - public VM getVM() { + public Jre getJre() { if (buildEnvironment == null) { throw new IllegalArgumentException("No Gradle build available"); } - return new VM(getJavaRuntimeVersion(), buildEnvironment.getJava().getJavaHome().toPath().toString()); + return new Jre(getJavaRuntimeVersion(), buildEnvironment.getJava().getJavaHome().toPath().toString()); } public String getGradleVersion() throws GradleException { diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathData.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathData.java index 0e0b1983cd..50fe162006 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathData.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathData.java @@ -19,7 +19,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.protocol.java.Classpath.CPE; -import org.springframework.ide.vscode.commons.protocol.java.VM; +import org.springframework.ide.vscode.commons.protocol.java.Jre; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; @@ -37,16 +37,16 @@ public class ClasspathData implements IClasspath { private String name; private Set classpathEntries; - private VM vm; + private Jre jre; private Cache> binaryLibLookupCache; public ClasspathData() {} - public ClasspathData(String name, Collection classpathEntries, VM vm) { + public ClasspathData(String name, Collection classpathEntries, Jre jre) { this.name = name; this.classpathEntries = ImmutableSet.copyOf(classpathEntries); - this.vm = vm; + this.jre = jre; this.binaryLibLookupCache = CacheBuilder.newBuilder().build(); } @@ -61,7 +61,7 @@ public static ClasspathData from(IClasspath d) { return new ClasspathData( d.getName(), entries==null ? ImmutableSet.of() : entries, - d.getVM() + d.getJre() ); } @@ -75,12 +75,12 @@ public void setName(String name) { } @Override - public VM getVM() { - return vm; + public Jre getJre() { + return jre; } - public void setVM(VM javaVersion) { - this.vm = vm; + public void setJre(Jre jre) { + this.jre = jre; } @Override diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java index 581e2baac8..517c9a79b6 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java @@ -14,7 +14,7 @@ import java.util.concurrent.atomic.AtomicReference; import org.springframework.ide.vscode.commons.protocol.java.Classpath.CPE; -import org.springframework.ide.vscode.commons.protocol.java.VM; +import org.springframework.ide.vscode.commons.protocol.java.Jre; import org.springframework.ide.vscode.commons.util.Assert; import com.google.common.base.Objects; @@ -82,8 +82,8 @@ public ImmutableList getClasspathEntries() throws Exception { } @Override - public VM getVM() { - return cachedData.get().getVM(); + public Jre getJre() { + return cachedData.get().getJre(); } public boolean isCached() { diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java index c18cf562c3..af0b2f67b7 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java @@ -18,7 +18,7 @@ import org.slf4j.LoggerFactory; 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.VM; +import org.springframework.ide.vscode.commons.protocol.java.Jre; /** * Classpath for a Java artifact @@ -63,6 +63,6 @@ default Optional findBinaryLibrary(String prefix) { * VM info * @return returns java version */ - VM getVM(); + Jre getJre(); } diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/Classpath.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/Classpath.java index 4bd196c7e6..9e362160fb 100644 --- a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/Classpath.java +++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/Classpath.java @@ -31,11 +31,11 @@ public class Classpath { public static final Classpath EMPTY = new Classpath(Collections.emptyList(), null); private List entries; - private VM vm; + private Jre jre; - public Classpath(List entries, VM vm) { + public Classpath(List entries, Jre jre) { this.entries = entries; - this.vm = vm; + this.jre = jre; } public List getEntries() { @@ -46,17 +46,17 @@ public void setEntries(List entries) { this.entries = entries; } - public VM getVM() { - return vm; + public Jre getJre() { + return jre; } - public void setVM(VM vm) { - this.vm = vm; + public void setJre(Jre jre) { + this.jre = jre; } @Override public String toString() { - return "Classpath [entries=" + entries + ", vm=" + vm + "]"; + return "Classpath [entries=" + entries + ", jre=" + jre + "]"; } public static class CPE { diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/VM.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/Jre.java similarity index 90% rename from headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/VM.java rename to headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/Jre.java index 52b3bfaa9a..e15aa7472a 100644 --- a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/VM.java +++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/Jre.java @@ -10,5 +10,5 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.protocol.java; -public record VM(String version, String installationPath) { +public record Jre(String version, String installationPath) { } diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java index 908843b005..e73e94c9dd 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java @@ -11,13 +11,11 @@ package org.springframework.ide.vscode.commons.maven.java; import java.io.File; -import java.net.URL; +import java.net.URI; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; -import java.util.List; import java.util.Set; import org.apache.maven.artifact.Artifact; @@ -31,7 +29,7 @@ import org.springframework.ide.vscode.commons.maven.MavenCore; import org.springframework.ide.vscode.commons.maven.MavenException; import org.springframework.ide.vscode.commons.protocol.java.Classpath.CPE; -import org.springframework.ide.vscode.commons.protocol.java.VM; +import org.springframework.ide.vscode.commons.protocol.java.Jre; import org.springframework.ide.vscode.commons.util.RunnableWithException; import com.google.common.base.Objects; @@ -93,7 +91,7 @@ private ImmutableList resolveClasspathEntries(MavenProject project) throws if (javaVersion == null) { javaVersion = "8"; } - cpe.setJavadocContainerUrl(new URL("https://docs.oracle.com/javase/" + javaVersion + "/docs/api/")); + cpe.setJavadocContainerUrl(URI.create("https://docs.oracle.com/javase/" + javaVersion + "/docs/api/").toURL()); cpe.setSystem(true); entries.add(cpe); // Add at the end, not critical if throws exception, but the CPE needs to be around regardless if the below throws @@ -230,22 +228,14 @@ public ImmutableList getClasspathEntries() throws Exception { } @Override - public VM getVM() { - return cachedData.getVM() != null ? cachedData.getVM() : null; + public Jre getJre() { + return cachedData.getJre() != null ? cachedData.getJre() : null; } private Set projectDependencies(MavenProject project) { return project == null ? Collections.emptySet() : project.getArtifacts(); } - private List projectOutput(MavenProject project) { - if (project == null) { - return Collections.emptyList(); - } else { - return Arrays.asList(new File(project.getBuild().getOutputDirectory()), new File(project.getBuild().getTestOutputDirectory())); - } - } - private static void safe(RunnableWithException do_stuff) { try { do_stuff.run(); @@ -262,7 +252,7 @@ private ClasspathData createClasspathData() throws Exception { String javaVersion = maven.getJavaRuntimeVersion(); String javaHome = maven.getJavaHome(); - return new ClasspathData(name, new LinkedHashSet<>(entries), new VM(javaVersion, Paths.get(javaHome).toString())); + return new ClasspathData(name, new LinkedHashSet<>(entries), new Jre(javaVersion, Paths.get(javaHome).toString())); } @Override diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java index 03a2963a73..7e98eb04ca 100644 --- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java @@ -43,8 +43,8 @@ import org.eclipse.m2e.core.MavenPlugin; 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.Jre; import org.springframework.ide.vscode.commons.protocol.java.ProjectBuild; -import org.springframework.ide.vscode.commons.protocol.java.VM; import org.springframework.tooling.jdt.ls.commons.Logger; @SuppressWarnings("restriction") @@ -104,7 +104,7 @@ public static Classpath resolve(IJavaProject javaProject, Logger logger) throws } String javaVersion = vmInstall instanceof IVMInstall2 ? ((IVMInstall2) vmInstall).getJavaVersion() : null; String installationPath = vmInstall == null ? null : vmInstall.getInstallLocation().toPath().toString(); - Classpath classpath = new Classpath(cpEntries, vmInstall == null ? null : new VM(javaVersion, installationPath)); + Classpath classpath = new Classpath(cpEntries, vmInstall == null ? null : new Jre(javaVersion, installationPath)); logger.debug("classpath=" + classpath.getEntries().size() + " entries"); return classpath; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/commands/WorkspaceBootExecutableProjects.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/commands/WorkspaceBootExecutableProjects.java index a5ee1f61e0..8af9668739 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/commands/WorkspaceBootExecutableProjects.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/commands/WorkspaceBootExecutableProjects.java @@ -138,7 +138,7 @@ private CompletableFuture> mapToBootProjectInfo(IJavaP String appBean = bootAppBeans.get(0) != null ? bootAppBeans.get(0).getType() : null; String springBootVersion = SpringProjectUtil.getSpringBootVersion(project).toString(); String buildTool = project.getProjectBuild().getType(); - String javaVersion = project.getClasspath().getVM() == null ? null : project.getClasspath().getVM().version(); + String javaVersion = project.getClasspath().getJre() == null ? null : project.getClasspath().getJre().version(); return Optional .of(new BootProjectInfo(project.getElementName(), project.getLocationUri().toASCIIString(), appBean, buildTool, springBootVersion, javaVersion)); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java index 650bf35c1e..06918cedeb 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java @@ -1054,7 +1054,7 @@ private WorkspaceSymbol provideDefaultSymbol(Annotation node, final SpringIndexe public static ASTParserCleanupEnabled createParser(IJavaProject project, AnnotationHierarchies annotationHierarchies, boolean ignoreMethodBodies) throws Exception { String[] classpathEntries = getClasspathEntries(project); String[] sourceEntries = getSourceEntries(project); - String complianceJavaVersion = getComplianceJavaVersion(project.getClasspath().getVM() == null ? null : project.getClasspath().getVM().version()); + String complianceJavaVersion = getComplianceJavaVersion(project.getClasspath().getJre() == null ? null : project.getClasspath().getJre().version()); return new ASTParserCleanupEnabled(classpathEntries, sourceEntries, complianceJavaVersion, annotationHierarchies, ignoreMethodBodies); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java index 0b647cad76..4e896b07a1 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java @@ -370,7 +370,7 @@ public synchronized void changed(Event event) { } else { log.debug("deleted = false"); URI projectUri = new URI(uri); - ClasspathData classpath = new ClasspathData(event.name, event.classpath.getEntries(), event.classpath.getVM()); + ClasspathData classpath = new ClasspathData(event.name, event.classpath.getEntries(), event.classpath.getJre()); IJavaProject oldProject, newProject; synchronized(table) { oldProject = table.get(uri); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/ProjectInformation.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/ProjectInformation.java index bae3d8fc2f..eed0045f4e 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/ProjectInformation.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/ProjectInformation.java @@ -44,7 +44,7 @@ public ProjectInformation(JavaProjectFinder projectFinder) { public List getProjectList() throws Exception { return projectFinder.all() .stream() - .map(project -> new Project(project.getElementName(), SpringProjectUtil.isBootProject(project), project.getClasspath().getVM() == null ? null : project.getClasspath().getVM().version())) + .map(project -> new Project(project.getElementName(), SpringProjectUtil.isBootProject(project), project.getClasspath().getJre() == null ? null : project.getClasspath().getJre().version())) .toList(); } @@ -77,7 +77,7 @@ public String getJavaVersion( IJavaProject project = getProject(projectName); IClasspath classpath = project.getClasspath(); - return classpath.getVM().version(); + return classpath.getJre().version(); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/modulith/ModulithService.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/modulith/ModulithService.java index 41be712c71..5b6cadd911 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/modulith/ModulithService.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/modulith/ModulithService.java @@ -53,7 +53,7 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory.Toggle.Option; 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.VM; +import org.springframework.ide.vscode.commons.protocol.java.Jre; import org.springframework.ide.vscode.commons.protocol.spring.BeansParams; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -325,9 +325,9 @@ private CompletableFuture loadModulesMetadata(IJavaProject project, return findRootPackages(project, delay).thenComposeAsync(packages -> { if (!packages.isEmpty()) { try { - VM vm = project.getClasspath().getVM(); - String exec = vm == null ? ProcessHandle.current().info().command().orElseThrow() - : JavaUtils.findJavaExecutable(Paths.get(vm.installationPath()).toFile()).toString(); + Jre jre = project.getClasspath().getJre(); + String exec = jre == null ? ProcessHandle.current().info().command().orElseThrow() + : JavaUtils.findJavaExecutable(Paths.get(jre.installationPath()).toFile()).toString(); String classpathStr = project.getClasspath().getClasspathEntries().stream().map(cpe -> { if (Classpath.ENTRY_KIND_SOURCE.equals(cpe.getKind())) { return cpe.getOutputFolder(); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/MockProjects.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/MockProjects.java index cca7e801f8..3f96e90e84 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/MockProjects.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/MockProjects.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018, 2022 Pivotal, Inc. + * Copyright (c) 2018, 2025 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 @@ -15,8 +15,10 @@ import java.io.ByteArrayInputStream; import java.io.File; +import java.io.IOException; import java.net.URI; import java.nio.file.FileSystems; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.PathMatcher; import java.util.ArrayList; @@ -39,12 +41,10 @@ import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver.Listener; 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.VM; +import org.springframework.ide.vscode.commons.protocol.java.Jre; import org.springframework.ide.vscode.commons.util.FileObserver; import org.springframework.ide.vscode.commons.util.IOUtil; -import com.google.common.io.Files; - public class MockProjects { private final Map projectsByName = new HashMap(); @@ -103,7 +103,7 @@ public Collection getClasspathEntries() throws Exception { } @Override - public VM getVM() { + public Jre getJre() { // return javaVersion; return null; } @@ -114,11 +114,15 @@ public MockProject(String name) { assertFalse(projectsByName.containsKey(name)); this.name = name; // this.javaVersion = ""; - this.root = Files.createTempDir(); - createSourceFolder("src/main/java"); - createSourceFolder("src/main/resources"); - createOutputFolder("target/classes"); - projectsByName.put(name, this); + try { + this.root = Files.createTempDirectory(name).toFile(); + createSourceFolder("src/main/java"); + createSourceFolder("src/main/resources"); + createOutputFolder("target/classes"); + projectsByName.put(name, this); + } catch (IOException e) { + throw new IllegalStateException(e); + } } synchronized (observer.listeners) { for (Listener l : observer.listeners) { diff --git a/vscode-extensions/vscode-spring-boot/lib/copilot/springBootAgent.ts b/vscode-extensions/vscode-spring-boot/lib/copilot/springBootAgent.ts index 3d544a41cf..279fa142ec 100644 --- a/vscode-extensions/vscode-spring-boot/lib/copilot/springBootAgent.ts +++ b/vscode-extensions/vscode-spring-boot/lib/copilot/springBootAgent.ts @@ -14,12 +14,7 @@ interface BootProjectInfo { mainClass: string; buildTool: string; springBootVersion: string; - vm: VM; -} - -interface VM { - version: string; - installationPath: string; + javaVersion: string; } interface SpringBootChatAgentResult extends ChatResult { @@ -54,7 +49,7 @@ export default class SpringBootChatAgent { Root Package name: ${bootProjInfo.mainClass.substring(0, bootProjInfo.mainClass.lastIndexOf('.'))} Build tool: ${bootProjInfo.buildTool} Spring Boot version: ${bootProjInfo.springBootVersion} - Java version: ${bootProjInfo?.vm.version} + Java version: ${bootProjInfo.javaVersion} User prompt: ${request.prompt} `;