Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
package com.microsoft.java.debug.core.adapter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
Expand All @@ -25,11 +27,17 @@
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.jar.Attributes;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import com.microsoft.java.debug.core.DebugException;
Expand Down Expand Up @@ -287,4 +295,52 @@ public static String decodeURIComponent(String uri) {
return uri;
}
}

/**
* Generate the classpath parameters to a temporary classpath.jar.
* @param classPaths - the classpath parameters
* @return the file path of the generate classpath.jar
* @throws IOException Some errors occur during generating the classpath.jar
*/
public static Path generateClasspathJar(String[] classPaths) throws IOException {
List<String> classpathUrls = new ArrayList<>();
for (String classpath : classPaths) {
classpathUrls.add(AdapterUtils.toUrl(classpath));
}

Manifest manifest = new Manifest();
Attributes attributes = manifest.getMainAttributes();
attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
// In jar manifest, the absolute path C:\a.jar should be converted to the url style file:///C:/a.jar
attributes.put(Attributes.Name.CLASS_PATH, String.join(" ", classpathUrls));
Path tempfile = Files.createTempFile("classpath_", ".jar");
JarOutputStream jar = new JarOutputStream(new FileOutputStream(tempfile.toFile()), manifest);
jar.close();

return tempfile;
}

/**
* Generate the classpath parameters to a temporary argfile file.
* @param classPaths - the classpath parameters
* @param modulePaths - the modulepath parameters
* @return the file path of the generated argfile
* @throws IOException Some errors occur during generating the argfile
*/
public static Path generateArgfile(String[] classPaths, String[] modulePaths) throws IOException {
String argfile = "";
if (ArrayUtils.isNotEmpty(classPaths)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the path separate cross-platform safety?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mean File.pathSeparator? it's platform dependent.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, lots of hard code with path manipulation. Should aware of platform for path operations.

argfile = "-classpath \"" + String.join(File.pathSeparator, classPaths) + "\"";
}

if (ArrayUtils.isNotEmpty(modulePaths)) {
argfile = " --module-path \"" + String.join(File.pathSeparator, modulePaths) + "\"";
}

argfile = argfile.replace("\\", "\\\\");
Path tempfile = Files.createTempFile("java_", ".argfile");
Files.write(tempfile, argfile.getBytes());

return tempfile;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand Down Expand Up @@ -86,7 +87,7 @@ private void destroyLaunchFiles(IDebugAdapterContext context) {
}

try {
Thread.sleep(1000);
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// do nothing.
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@
package com.microsoft.java.debug.core.adapter.handler;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -26,9 +24,6 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import java.util.jar.Attributes;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand Down Expand Up @@ -97,59 +92,31 @@ protected CompletableFuture<Response> handleLaunchCommand(Arguments arguments, R

activeLaunchHandler.preLaunch(launchArguments, context);

Path tempfile = null;
// Use the specified cli style to launch the program.
if (launchArguments.shortenCommandLine == ShortenApproach.JARMANIFEST) {
if (ArrayUtils.isNotEmpty(launchArguments.classPaths)) {
List<String> classpathUrls = new ArrayList<>();
for (String classpath : launchArguments.classPaths) {
try {
classpathUrls.add(AdapterUtils.toUrl(classpath));
} catch (IllegalArgumentException | MalformedURLException ex) {
logger.log(Level.SEVERE, String.format("Failed to launch the program with jarmanifest style: %s", ex.toString(), ex));
throw AdapterUtils.createCompletionException("Failed to launch the program with jarmanifest style: " + ex.toString(),
ErrorCode.LAUNCH_FAILURE, ex);
}
}

try {
tempfile = Files.createTempFile("classpath_", ".jar");
Manifest manifest = new Manifest();
Attributes attributes = manifest.getMainAttributes();
attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
// In jar manifest, the absolute path C:\a.jar should be converted to the url style file:///C:/a.jar
attributes.put(Attributes.Name.CLASS_PATH, String.join(" ", classpathUrls));
JarOutputStream jar = new JarOutputStream(new FileOutputStream(tempfile.toFile()), manifest);
jar.close();
Path tempfile = AdapterUtils.generateClasspathJar(launchArguments.classPaths);
launchArguments.vmArgs += " -cp \"" + tempfile.toAbsolutePath().toString() + "\"";
launchArguments.classPaths = new String[0];
context.setClasspathJar(tempfile);
} catch (IllegalArgumentException | MalformedURLException ex) {
logger.log(Level.SEVERE, String.format("Failed to launch the program with jarmanifest style: %s", ex.toString(), ex));
throw AdapterUtils.createCompletionException("Failed to launch the program with jarmanifest style: " + ex.toString(),
ErrorCode.LAUNCH_FAILURE, ex);
} catch (IOException e) {
logger.log(Level.SEVERE, String.format("Failed to create a temp classpath.jar: %s", e.toString()), e);
tempfile = null;
}
}
} else if (launchArguments.shortenCommandLine == ShortenApproach.ARGFILE) {
try {
tempfile = Files.createTempFile("java_", ".argfile");
String argfile = "";
if (ArrayUtils.isNotEmpty(launchArguments.classPaths)) {
argfile = "-classpath \"" + String.join(File.pathSeparator, launchArguments.classPaths) + "\"";
}

if (ArrayUtils.isNotEmpty(launchArguments.modulePaths)) {
argfile = " --module-path \"" + String.join(File.pathSeparator, launchArguments.modulePaths) + "\"";
}

argfile = argfile.replace("\\", "\\\\");
Files.write(tempfile, argfile.getBytes());
Path tempfile = AdapterUtils.generateArgfile(launchArguments.classPaths, launchArguments.modulePaths);
launchArguments.vmArgs += " @" + tempfile.toAbsolutePath().toString();
launchArguments.classPaths = new String[0];
launchArguments.modulePaths = new String[0];
context.setArgsfile(tempfile);
} catch (IOException e) {
logger.log(Level.SEVERE, String.format("Failed to create a temp argfile: %s", e.toString()), e);
tempfile = null;
}
}

Expand All @@ -161,7 +128,14 @@ protected CompletableFuture<Response> handleLaunchCommand(Arguments arguments, R
});
}

protected static String[] constructLaunchCommands(LaunchArguments launchArguments, boolean serverMode, String address) {
/**
* Construct the Java command lines based on the given launch arguments.
* @param launchArguments - The launch arguments
* @param serverMode - whether to enable the debug port with server mode
* @param address - the debug port
* @return the command arrays
*/
public static String[] constructLaunchCommands(LaunchArguments launchArguments, boolean serverMode, String address) {
String slash = System.getProperty("file.separator");

List<String> launchCmds = new ArrayList<>();
Expand Down
9 changes: 3 additions & 6 deletions com.microsoft.java.debug.plugin/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@
<command id="vscode.java.buildWorkspace"/>
<command id="vscode.java.fetchUsageData"/>
<command id="vscode.java.updateDebugSettings"/>
<command
id="vscode.java.validateLaunchConfig">
</command>
<command
id="vscode.java.resolveMainMethod">
</command>
<command id="vscode.java.validateLaunchConfig"/>
<command id="vscode.java.resolveMainMethod"/>
<command id="vscode.java.inferLaunchCommandLength"/>
</delegateCommandHandler>
</extension>
</plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -17,47 +17,48 @@
import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler;

import com.microsoft.java.debug.core.UsageDataStore;
import com.microsoft.java.debug.core.protocol.JsonUtils;
import com.microsoft.java.debug.core.protocol.Requests.LaunchArguments;

public class JavaDebugDelegateCommandHandler implements IDelegateCommandHandler {

public static String FETCH_USER_DATA = "vscode.java.fetchUsageData";

public static String DEBUG_STARTSESSION = "vscode.java.startDebugSession";

public static String RESOLVE_CLASSPATH = "vscode.java.resolveClasspath";

public static String RESOLVE_MAINCLASS = "vscode.java.resolveMainClass";

public static String BUILD_WORKSPACE = "vscode.java.buildWorkspace";

public static String UPDATE_DEBUG_SETTINGS = "vscode.java.updateDebugSettings";

public static String VALIDATE_LAUNCHCONFIG = "vscode.java.validateLaunchConfig";

public static String RESOLVE_MAINMETHOD = "vscode.java.resolveMainMethod";
public static final String FETCH_USER_DATA = "vscode.java.fetchUsageData";
public static final String DEBUG_STARTSESSION = "vscode.java.startDebugSession";
public static final String RESOLVE_CLASSPATH = "vscode.java.resolveClasspath";
public static final String RESOLVE_MAINCLASS = "vscode.java.resolveMainClass";
public static final String BUILD_WORKSPACE = "vscode.java.buildWorkspace";
public static final String UPDATE_DEBUG_SETTINGS = "vscode.java.updateDebugSettings";
public static final String VALIDATE_LAUNCHCONFIG = "vscode.java.validateLaunchConfig";
public static final String RESOLVE_MAINMETHOD = "vscode.java.resolveMainMethod";
public static final String INFER_LAUNCH_COMMAND_LENGTH = "vscode.java.inferLaunchCommandLength";

@Override
public Object executeCommand(String commandId, List<Object> arguments, IProgressMonitor progress) throws Exception {
if (DEBUG_STARTSESSION.equals(commandId)) {
IDebugServer debugServer = JavaDebugServer.getInstance();
debugServer.start();
return debugServer.getPort();
} else if (RESOLVE_CLASSPATH.equals(commandId)) {
ResolveClasspathsHandler handler = new ResolveClasspathsHandler();
return handler.resolveClasspaths(arguments);
} else if (RESOLVE_MAINCLASS.equals(commandId)) {
ResolveMainClassHandler handler = new ResolveMainClassHandler();
return handler.resolveMainClass(arguments);
} else if (BUILD_WORKSPACE.equals(commandId)) {
// TODO
} else if (FETCH_USER_DATA.equals(commandId)) {
return UsageDataStore.getInstance().fetchAll();
} else if (UPDATE_DEBUG_SETTINGS.equals(commandId)) {
return DebugSettingUtils.configDebugSettings(arguments);
} else if (VALIDATE_LAUNCHCONFIG.equals(commandId)) {
return new ResolveMainClassHandler().validateLaunchConfig(arguments);
} else if (RESOLVE_MAINMETHOD.equals(commandId)) {
return ResolveMainMethodHandler.resolveMainMethods(arguments);
switch (commandId) {
case DEBUG_STARTSESSION:
IDebugServer debugServer = JavaDebugServer.getInstance();
debugServer.start();
return debugServer.getPort();
case RESOLVE_CLASSPATH:
ResolveClasspathsHandler handler = new ResolveClasspathsHandler();
return handler.resolveClasspaths(arguments);
case RESOLVE_MAINCLASS:
ResolveMainClassHandler resolveMainClassHandler = new ResolveMainClassHandler();
return resolveMainClassHandler.resolveMainClass(arguments);
case BUILD_WORKSPACE:
// TODO
break;
case FETCH_USER_DATA:
return UsageDataStore.getInstance().fetchAll();
case UPDATE_DEBUG_SETTINGS:
return DebugSettingUtils.configDebugSettings(arguments);
case VALIDATE_LAUNCHCONFIG:
return new ResolveMainClassHandler().validateLaunchConfig(arguments);
case RESOLVE_MAINMETHOD:
return ResolveMainMethodHandler.resolveMainMethods(arguments);
case INFER_LAUNCH_COMMAND_LENGTH:
return LaunchCommandHandler.getLaunchCommandLength(JsonUtils.fromJson((String) arguments.get(0), LaunchArguments.class));
default:
break;
}

throw new UnsupportedOperationException(String.format("Java debug plugin doesn't support the command '%s'.", commandId));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*******************************************************************************
* Copyright (c) 2019 Microsoft Corporation and others.
* 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Microsoft Corporation - initial API and implementation
*******************************************************************************/

package com.microsoft.java.debug.plugin.internal;

import java.nio.charset.StandardCharsets;

import org.apache.commons.lang3.StringUtils;

import com.microsoft.java.debug.core.adapter.handler.LaunchRequestHandler;
import com.microsoft.java.debug.core.protocol.Requests.CONSOLE;
import com.microsoft.java.debug.core.protocol.Requests.LaunchArguments;

public class LaunchCommandHandler {

/**
* Get the approximate command line length based on the launch arguments.
* @param launchArguments - the launch arguments
* @return the approximate command line length
*/
public static int getLaunchCommandLength(LaunchArguments launchArguments) {
String encoding = StringUtils.isBlank(launchArguments.encoding) ? StandardCharsets.UTF_8.name() : launchArguments.encoding;
launchArguments.vmArgs += String.format(" -Dfile.encoding=%s", encoding);
String address = launchArguments.noDebug ? "" : "888888";
String[] commands = LaunchRequestHandler.constructLaunchCommands(launchArguments, false, address);
int cwdLength = launchArguments.console == CONSOLE.internalConsole ? 0 : StringUtils.length("cd " + launchArguments.cwd + " && ");
return cwdLength + String.join(" ", commands).length();
}

}