Skip to content

Commit cf338ee

Browse files
Use project's java runtime to launch the application (#319)
* Use project's java runtime to launch the application Signed-off-by: Jinbo Wang <[email protected]> * make checkstyle happy Signed-off-by: Jinbo Wang <[email protected]> * address review comments Signed-off-by: Jinbo Wang <[email protected]>
1 parent c2b51f1 commit cf338ee

File tree

7 files changed

+210
-7
lines changed

7 files changed

+210
-7
lines changed

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugUtility.java

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
import java.net.URLDecoder;
1818
import java.net.URLEncoder;
1919
import java.nio.charset.StandardCharsets;
20+
import java.nio.file.Files;
2021
import java.util.ArrayList;
2122
import java.util.List;
2223
import java.util.Map;
24+
import java.util.Objects;
2325
import java.util.concurrent.CompletableFuture;
2426

2527
import org.apache.commons.lang3.StringUtils;
@@ -57,7 +59,7 @@ public class DebugUtility {
5759

5860
/**
5961
* Launch a debuggee in suspend mode.
60-
* @see #launch(VirtualMachineManager, String, String, String, String, String)
62+
* @see #launch(VirtualMachineManager, String, String, String, String, String, String, String[])
6163
*/
6264
public static IDebugSession launch(VirtualMachineManager vmManager,
6365
String mainClass,
@@ -78,6 +80,31 @@ public static IDebugSession launch(VirtualMachineManager vmManager,
7880
envVars);
7981
}
8082

83+
/**
84+
* Launch a debuggee in suspend mode.
85+
* @see #launch(VirtualMachineManager, String, String, String, String, String, String, String[], String)
86+
*/
87+
public static IDebugSession launch(VirtualMachineManager vmManager,
88+
String mainClass,
89+
String programArguments,
90+
String vmArguments,
91+
List<String> modulePaths,
92+
List<String> classPaths,
93+
String cwd,
94+
String[] envVars,
95+
String javaExec)
96+
throws IOException, IllegalConnectorArgumentsException, VMStartException {
97+
return DebugUtility.launch(vmManager,
98+
mainClass,
99+
programArguments,
100+
vmArguments,
101+
String.join(File.pathSeparator, modulePaths),
102+
String.join(File.pathSeparator, classPaths),
103+
cwd,
104+
envVars,
105+
javaExec);
106+
}
107+
81108
/**
82109
* Launches a debuggee in suspend mode.
83110
*
@@ -116,6 +143,50 @@ public static IDebugSession launch(VirtualMachineManager vmManager,
116143
String cwd,
117144
String[] envVars)
118145
throws IOException, IllegalConnectorArgumentsException, VMStartException {
146+
return launch(vmManager, mainClass, programArguments, vmArguments, modulePaths, classPaths, cwd, envVars, null);
147+
}
148+
149+
/**
150+
* Launches a debuggee in suspend mode.
151+
*
152+
* @param vmManager
153+
* the virtual machine manager.
154+
* @param mainClass
155+
* the main class.
156+
* @param programArguments
157+
* the program arguments.
158+
* @param vmArguments
159+
* the vm arguments.
160+
* @param modulePaths
161+
* the module paths.
162+
* @param classPaths
163+
* the class paths.
164+
* @param cwd
165+
* the working directory of the program.
166+
* @param envVars
167+
* array of strings, each element of which has environment variable settings in the format name=value.
168+
* or null if the subprocess should inherit the environment of the current process.
169+
* @param javaExec
170+
* the java executable path. If not defined, then resolve from java home.
171+
* @return an instance of IDebugSession.
172+
* @throws IOException
173+
* when unable to launch.
174+
* @throws IllegalConnectorArgumentsException
175+
* when one of the arguments is invalid.
176+
* @throws VMStartException
177+
* when the debuggee was successfully launched, but terminated
178+
* with an error before a connection could be established.
179+
*/
180+
public static IDebugSession launch(VirtualMachineManager vmManager,
181+
String mainClass,
182+
String programArguments,
183+
String vmArguments,
184+
String modulePaths,
185+
String classPaths,
186+
String cwd,
187+
String[] envVars,
188+
String javaExec)
189+
throws IOException, IllegalConnectorArgumentsException, VMStartException {
119190
List<LaunchingConnector> connectors = vmManager.launchingConnectors();
120191
LaunchingConnector connector = connectors.get(0);
121192

@@ -164,7 +235,12 @@ public static IDebugSession launch(VirtualMachineManager vmManager,
164235
arguments.get(ENV).setValue(encodeArrayArgument(envVars));
165236
}
166237

167-
if (StringUtils.isNotEmpty(DebugSettings.getCurrent().javaHome)) {
238+
if (isValidJavaExec(javaExec)) {
239+
String vmExec = new File(javaExec).getName();
240+
String javaHome = new File(javaExec).getParentFile().getParentFile().getAbsolutePath();
241+
arguments.get(HOME).setValue(javaHome);
242+
arguments.get(EXEC).setValue(vmExec);
243+
} else if (StringUtils.isNotEmpty(DebugSettings.getCurrent().javaHome)) {
168244
arguments.get(HOME).setValue(DebugSettings.getCurrent().javaHome);
169245
}
170246

@@ -179,6 +255,20 @@ public static IDebugSession launch(VirtualMachineManager vmManager,
179255
return new DebugSession(vm);
180256
}
181257

258+
private static boolean isValidJavaExec(String javaExec) {
259+
if (StringUtils.isBlank(javaExec)) {
260+
return false;
261+
}
262+
263+
File file = new File(javaExec);
264+
if (!file.exists() || !file.isFile()) {
265+
return false;
266+
}
267+
268+
return Files.isExecutable(file.toPath())
269+
&& Objects.equals(file.getParentFile().getName(), "bin");
270+
}
271+
182272
/**
183273
* Attach to an existing debuggee VM.
184274
* @param vmManager

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.nio.charset.Charset;
1919
import java.nio.charset.StandardCharsets;
2020
import java.nio.file.Path;
21+
import java.nio.file.Paths;
2122
import java.util.ArrayList;
2223
import java.util.Arrays;
2324
import java.util.HashMap;
@@ -183,14 +184,18 @@ protected void handleTerminatedEvent(IDebugAdapterContext context) {
183184
* @return the command arrays
184185
*/
185186
public static String[] constructLaunchCommands(LaunchArguments launchArguments, boolean serverMode, String address) {
186-
String slash = System.getProperty("file.separator");
187187
List<String> launchCmds = new ArrayList<>();
188188
if (launchArguments.launcherScript != null) {
189189
launchCmds.add(launchArguments.launcherScript);
190190
}
191-
final String javaHome = StringUtils.isNotEmpty(DebugSettings.getCurrent().javaHome) ? DebugSettings.getCurrent().javaHome
192-
: System.getProperty("java.home");
193-
launchCmds.add(javaHome + slash + "bin" + slash + "java");
191+
192+
if (StringUtils.isNotBlank(launchArguments.javaExec)) {
193+
launchCmds.add(launchArguments.javaExec);
194+
} else {
195+
final String javaHome = StringUtils.isNotEmpty(DebugSettings.getCurrent().javaHome) ? DebugSettings.getCurrent().javaHome
196+
: System.getProperty("java.home");
197+
launchCmds.add(Paths.get(javaHome, "bin", "java").toString());
198+
}
194199
if (StringUtils.isNotEmpty(address)) {
195200
launchCmds.add(String.format("-agentlib:jdwp=transport=dt_socket,server=%s,suspend=y,address=%s", serverMode ? "y" : "n", address));
196201
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchWithDebuggingDelegate.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ public Process launch(LaunchArguments launchArguments, IDebugAdapterContext cont
181181
Arrays.asList(launchArguments.modulePaths),
182182
Arrays.asList(launchArguments.classPaths),
183183
launchArguments.cwd,
184-
LaunchRequestHandler.constructEnvironmentVariables(launchArguments));
184+
LaunchRequestHandler.constructEnvironmentVariables(launchArguments),
185+
launchArguments.javaExec);
185186
context.setDebugSession(debugSession);
186187

187188
logger.info("Launching debuggee VM succeeded.");

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Requests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public static class LaunchArguments extends LaunchBaseArguments {
8686
public CONSOLE console = CONSOLE.integratedTerminal;
8787
public ShortenApproach shortenCommandLine = ShortenApproach.NONE;
8888
public String launcherScript;
89+
public String javaExec;
8990
}
9091

9192
public static class AttachArguments extends LaunchBaseArguments {

com.microsoft.java.debug.plugin/plugin.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<command id="vscode.java.resolveElementAtSelection"/>
1717
<command id="vscode.java.resolveBuildFiles"/>
1818
<command id="vscode.java.isOnClasspath"/>
19+
<command id="vscode.java.resolveJavaExecutable"/>
1920
</delegateCommandHandler>
2021
</extension>
2122
</plugin>

com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JavaDebugDelegateCommandHandler.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public class JavaDebugDelegateCommandHandler implements IDelegateCommandHandler
4343
public static final String RESOLVE_ELEMENT_AT_SELECTION = "vscode.java.resolveElementAtSelection";
4444
public static final String RESOLVE_BUILD_FILES = "vscode.java.resolveBuildFiles";
4545
public static final String IS_ON_CLASSPATH = "vscode.java.isOnClasspath";
46+
public static final String RESOLVE_JAVA_EXECUTABLE = "vscode.java.resolveJavaExecutable";
4647

4748
@Override
4849
public Object executeCommand(String commandId, List<Object> arguments, IProgressMonitor progress) throws Exception {
@@ -78,6 +79,8 @@ public Object executeCommand(String commandId, List<Object> arguments, IProgress
7879
return getBuildFiles();
7980
case IS_ON_CLASSPATH:
8081
return isOnClasspath(arguments);
82+
case RESOLVE_JAVA_EXECUTABLE:
83+
return ResolveJavaExecutableHandler.resolveJavaExecutable(arguments);
8184
default:
8285
break;
8386
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2020 Microsoft Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Microsoft Corporation - initial API and implementation
10+
*******************************************************************************/
11+
12+
package com.microsoft.java.debug.plugin.internal;
13+
14+
import java.io.File;
15+
import java.nio.file.Paths;
16+
import java.util.List;
17+
import java.util.Objects;
18+
import java.util.logging.Level;
19+
import java.util.logging.Logger;
20+
21+
import org.apache.commons.lang3.StringUtils;
22+
import org.eclipse.core.runtime.CoreException;
23+
import org.eclipse.jdt.core.IJavaProject;
24+
import org.eclipse.jdt.launching.IVMInstall;
25+
import org.eclipse.jdt.launching.JavaRuntime;
26+
27+
import com.microsoft.java.debug.core.Configuration;
28+
29+
public class ResolveJavaExecutableHandler {
30+
private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME);
31+
private static final String[] javaExecCandidates = {
32+
"java",
33+
"java.exe",
34+
"javaw",
35+
"javaw.exe",
36+
"j9",
37+
"j9.exe",
38+
"j9w",
39+
"j9w.exe"
40+
};
41+
private static final String[] javaBinCandidates = {
42+
File.separator,
43+
"bin" + File.separatorChar,
44+
"jre" + File.separatorChar + "bin" + File.separatorChar
45+
};
46+
47+
/**
48+
* Resolve the java executable path from the project's java runtime.
49+
*/
50+
public static String resolveJavaExecutable(List<Object> arguments) throws Exception {
51+
try {
52+
String mainClass = (String) arguments.get(0);
53+
String projectName = (String) arguments.get(1);
54+
IJavaProject targetProject = null;
55+
if (StringUtils.isNotBlank(projectName)) {
56+
targetProject = JdtUtils.getJavaProject(projectName);
57+
} else {
58+
List<IJavaProject> targetProjects = ResolveClasspathsHandler.getJavaProjectFromType(mainClass);
59+
if (!targetProjects.isEmpty()) {
60+
targetProject = targetProjects.get(0);
61+
}
62+
}
63+
64+
if (targetProject == null) {
65+
return null;
66+
}
67+
68+
IVMInstall vmInstall = JavaRuntime.getVMInstall(targetProject);
69+
if (vmInstall == null || vmInstall.getInstallLocation() == null) {
70+
return null;
71+
}
72+
73+
File exe = findJavaExecutable(vmInstall.getInstallLocation());
74+
if (exe == null) {
75+
return null;
76+
}
77+
78+
return exe.getAbsolutePath();
79+
} catch (CoreException e) {
80+
logger.log(Level.SEVERE, "Failed to resolve java executable: " + e.getMessage(), e);
81+
}
82+
83+
return null;
84+
}
85+
86+
private static File findJavaExecutable(File vmInstallLocation) {
87+
boolean isBin = Objects.equals("bin", vmInstallLocation.getName());
88+
for (int i = 0; i < javaExecCandidates.length; i++) {
89+
for (int j = 0; j < javaBinCandidates.length; j++) {
90+
if (!isBin && j == 0) {
91+
continue;
92+
}
93+
File javaFile = new File(vmInstallLocation, Paths.get(javaBinCandidates[j], javaExecCandidates[i]).toString());
94+
if (javaFile.isFile()) {
95+
return javaFile;
96+
}
97+
}
98+
}
99+
100+
return null;
101+
}
102+
}

0 commit comments

Comments
 (0)