Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add capability to list all vm installs and update project jdk #2977

Merged
merged 2 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions org.eclipse.jdt.ls.core/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@
<command
id="java.project.resolveWorkspaceSymbol">
</command>
<command
id="java.project.updateJdk">
</command>
<command
id="java.protobuf.generateSources">
</command>
Expand All @@ -127,6 +130,9 @@
<command
id="java.edit.smartSemicolonDetection">
</command>
<command
id="java.vm.getAllInstalls">
</command>
</delegateCommandHandler>
</extension>
<extension
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017-2022 Microsoft Corporation and others.
* Copyright (c) 2017-2023 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -29,6 +29,7 @@
import org.eclipse.jdt.ls.core.internal.commands.ProjectCommand.ClasspathOptions;
import org.eclipse.jdt.ls.core.internal.commands.SourceAttachmentCommand;
import org.eclipse.jdt.ls.core.internal.commands.TypeHierarchyCommand;
import org.eclipse.jdt.ls.core.internal.commands.VmCommand;
import org.eclipse.jdt.ls.core.internal.framework.protobuf.ProtobufSupport;
import org.eclipse.jdt.ls.core.internal.handlers.BundleUtils;
import org.eclipse.jdt.ls.core.internal.handlers.CompletionHandler;
Expand Down Expand Up @@ -134,16 +135,22 @@ public Object executeCommand(String commandId, List<Object> arguments, IProgress
params.setPosition(textParams.getPosition());
TypeHierarchyItem typeHierarchyItem = typeHierarchyCommand.typeHierarchy(params, monitor);
return typeHierarchyItem;
case "java.project.upgradeGradle":
case "java.project.upgradeGradle": {
String projectUri = (String) arguments.get(0);
String gradleVersion = arguments.size() > 1 ? (String) arguments.get(1) : null;
if (gradleVersion == null) {
gradleVersion = GradleVersion.current().getVersion();
}
return GradleProjectImporter.upgradeGradleVersion(projectUri, gradleVersion, monitor);
}
case "java.project.resolveWorkspaceSymbol":
SymbolInformation si = JSONUtility.toModel(arguments.get(0), SymbolInformation.class);
return ProjectCommand.resolveWorkspaceSymbol(si);
case "java.project.updateJdk": {
String projectUri = (String) arguments.get(0);
String jdkPath = (String) arguments.get(1);
return ProjectCommand.updateProjectJdk(projectUri, jdkPath, monitor);
}
case "java.protobuf.generateSources":
ProtobufSupport.generateProtobufSources((ArrayList<String>) arguments.get(0), monitor);
return null;
Expand Down Expand Up @@ -177,6 +184,8 @@ public Object executeCommand(String commandId, List<Object> arguments, IProgress
}
SmartDetectionParams smartDetectionParams = JSONUtility.toModel(arguments.get(0), SmartDetectionParams.class);
return new SmartDetectionHandler(smartDetectionParams).getLocation(monitor);
case VmCommand.GET_ALL_INSTALL_COMMAND_ID:
return VmCommand.getAllVmInstalls();
default:
break;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020 Microsoft Corporation and others.
* Copyright (c) 2020-2023 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
Expand All @@ -26,6 +26,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
Expand All @@ -37,6 +38,7 @@
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
Expand All @@ -52,9 +54,13 @@
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.ClasspathEntry;
import org.eclipse.jdt.internal.launching.StandardVMType;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.IVMInstall2;
import org.eclipse.jdt.launching.IVMInstallType;
import org.eclipse.jdt.launching.JavaLaunchDelegate;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.VMStandin;
import org.eclipse.jdt.ls.core.internal.IConstants;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
Expand All @@ -76,17 +82,19 @@ public class ProjectCommand {
* Gets the project settings.
*
* @param uri
* Uri of the source/class file that needs to be queried.
* Uri of the source/class file that needs to be queried.
* @param settingKeys
* the settings we want to query, for example:
* ["org.eclipse.jdt.core.compiler.compliance",
* "org.eclipse.jdt.core.compiler.source"].
* Besides the options defined in JavaCore, the following keys can also be used:
* - "org.eclipse.jdt.ls.core.vm.location": Get the location of the VM assigned to build the given Java project
* - "org.eclipse.jdt.ls.core.sourcePaths": Get the source root paths of the given Java project
* - "org.eclipse.jdt.ls.core.outputPath": Get the default output path of the given Java project. Note that the default output path
* may not be equal to the output path of each source root.
* - "org.eclipse.jdt.ls.core.referencedLibraries": Get all the referenced library files of the given Java project
* the settings we want to query, for example:
* ["org.eclipse.jdt.core.compiler.compliance", "org.eclipse.jdt.core.compiler.source"].
* <p>
* Besides the options defined in JavaCore, the following keys can also be used:
* <ul>
* <li>"org.eclipse.jdt.ls.core.vm.location": Get the location of the VM assigned to build the given Java project.</li>
* <li>"org.eclipse.jdt.ls.core.sourcePaths": Get the source root paths of the given Java project.</li>
* <li>"org.eclipse.jdt.ls.core.outputPath": Get the default output path of the given Java project. Note that the default output path
* may not be equal to the output path of each source root.</li>
* <li>"org.eclipse.jdt.ls.core.referencedLibraries": Get all the referenced library files of the given Java project.</li>
* </ul>
* @return A <code>Map<string, string></code> with all the setting keys and
* their values.
* @throws CoreException
Expand Down Expand Up @@ -170,12 +178,9 @@ public static ClasspathResult getClasspathsFromJavaProject(IJavaProject javaProj
} else {
schedulingRule = javaProject.getSchedulingRule();
}
workspace.run(new IWorkspaceRunnable() {
@Override
public void run(IProgressMonitor monitor) throws CoreException {
String[][] paths = delegate.getClasspathAndModulepath(launchConfig);
result[0] = new ClasspathResult(javaProject.getProject().getLocationURI(), paths[0], paths[1]);
}
workspace.run((IWorkspaceRunnable) monitor -> {
String[][] paths = delegate.getClasspathAndModulepath(launchConfig);
result[0] = new ClasspathResult(javaProject.getProject().getLocationURI(), paths[0], paths[1]);
}, schedulingRule, IWorkspace.AVOID_UPDATE, new NullProgressMonitor());

if (result[0] != null) {
Expand Down Expand Up @@ -348,4 +353,82 @@ public static SymbolInformation resolveWorkspaceSymbol(SymbolInformation request

return si;
}

public static JdkUpdateResult updateProjectJdk(String projectUri, String jdkPath, IProgressMonitor monitor) throws CoreException, URISyntaxException {
IJavaProject javaProject = ProjectCommand.getJavaProjectFromUri(projectUri);
IClasspathEntry[] originalClasspathEntries = javaProject.getRawClasspath();
IClasspathAttribute[] extraAttributes = null;
List<IClasspathEntry> newClasspathEntries = new ArrayList<>();
for (IClasspathEntry entry : originalClasspathEntries) {
if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER &&
entry.getPath().toString().startsWith("org.eclipse.jdt.launching.JRE_CONTAINER")) {
extraAttributes = entry.getExtraAttributes();
} else {
newClasspathEntries.add(entry);
}
}

IVMInstall vmInstall = getVmInstallByPath(jdkPath);
if (vmInstall == null) {
JavaLanguageServerPlugin.log(new Status(IStatus.ERROR, IConstants.PLUGIN_ID, "The select JDK path is not valid."));
return new JdkUpdateResult(false, "The selected JDK path is not valid.");
}
newClasspathEntries.add(JavaCore.newContainerEntry(
JavaRuntime.newJREContainerPath(vmInstall),
ClasspathEntry.NO_ACCESS_RULES,
extraAttributes,
false /*isExported*/
));
javaProject.setRawClasspath(newClasspathEntries.toArray(IClasspathEntry[]::new), monitor);
return new JdkUpdateResult(true, vmInstall.getInstallLocation().getAbsolutePath());
}

private static IVMInstall getVmInstallByPath(String path) {
java.nio.file.Path vmPath = new Path(path).toPath();
IVMInstallType[] vmInstallTypes = JavaRuntime.getVMInstallTypes();
for (IVMInstallType vmInstallType : vmInstallTypes) {
IVMInstall[] vmInstalls = vmInstallType.getVMInstalls();
for (IVMInstall vmInstall : vmInstalls) {
if (vmInstall.getInstallLocation().toPath().normalize().compareTo(vmPath) == 0) {
return vmInstall;
}
}
}

StandardVMType standardType = (StandardVMType) JavaRuntime.getVMInstallType(StandardVMType.ID_STANDARD_VM_TYPE);
VMStandin vmStandin = new VMStandin(standardType, path);
File jdkHomeFile = vmPath.toFile();
vmStandin.setInstallLocation(jdkHomeFile);
String name = jdkHomeFile.getName();
int i = 1;
while (isDuplicateName(name)) {
name = jdkHomeFile.getName() + '(' + i++ + ')';
}
vmStandin.setName(name);
IVMInstall install = vmStandin.convertToRealVM();
if (!(install instanceof IVMInstall2 vm && vm.getJavaVersion() != null)) {
// worksaround: such VMs may cause issue later
// https://github.com/eclipse-jdt/eclipse.jdt.debug/issues/248
standardType.disposeVMInstall(install.getId());
return null;
}
return install;
}

private static boolean isDuplicateName(String name) {
return Stream.of(JavaRuntime.getVMInstallTypes()) //
.flatMap(vmType -> Arrays.stream(vmType.getVMInstalls())) //
.map(IVMInstall::getName) //
.anyMatch(name::equals);
}

public static final class JdkUpdateResult {
public boolean success;
public String message;

public JdkUpdateResult(boolean success, String message) {
this.success = success;
this.message = message;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*******************************************************************************
* Copyright (c) 2023 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Microsoft Corporation - initial API and implementation
*******************************************************************************/

package org.eclipse.jdt.ls.core.internal.commands;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.IVMInstall2;
import org.eclipse.jdt.launching.IVMInstallType;
import org.eclipse.jdt.launching.JavaRuntime;

public class VmCommand {
private VmCommand() {}

public static final String GET_ALL_INSTALL_COMMAND_ID = "java.vm.getAllInstalls";

/**
* List all available VM installs on the machine.
*/
public static final List<VmInstall> getAllVmInstalls() {
List<VmInstall> vmInstallList = new ArrayList<>();
IVMInstallType[] vmInstallTypes = JavaRuntime.getVMInstallTypes();
for (IVMInstallType vmInstallType : vmInstallTypes) {
IVMInstall[] vmInstalls = vmInstallType.getVMInstalls();
for (IVMInstall vmInstall : vmInstalls) {
VmInstall vm = new VmInstall(
vmInstallType.getName(),
vmInstall.getName(),
vmInstall.getInstallLocation().getAbsolutePath()
);
if (vmInstall instanceof IVMInstall2) {
vm.version = ((IVMInstall2) vmInstall).getJavaVersion();
}
vmInstallList.add(vm);
}
}
return vmInstallList;
}

public static final class VmInstall {
public String typeName;
public String name;
public String path;
public String version;

public VmInstall(String typeName, String name, String path) {
this.typeName = typeName;
this.name = name;
this.path = path;
}
}
}
Loading