-
Notifications
You must be signed in to change notification settings - Fork 185
Provide jarmanifest/argfile approachs to shorten the command line #253
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| /******************************************************************************* | ||
| * 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.core.adapter.handler; | ||
|
|
||
| import java.io.IOException; | ||
| import java.nio.file.Files; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.concurrent.CompletableFuture; | ||
| import java.util.logging.Level; | ||
| import java.util.logging.Logger; | ||
|
|
||
| import com.microsoft.java.debug.core.Configuration; | ||
| import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; | ||
| import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; | ||
| import com.microsoft.java.debug.core.adapter.LaunchMode; | ||
| import com.microsoft.java.debug.core.protocol.Messages.Response; | ||
| import com.microsoft.java.debug.core.protocol.Requests.Arguments; | ||
| import com.microsoft.java.debug.core.protocol.Requests.Command; | ||
|
|
||
| public abstract class AbstractDisconnectRequestHandler implements IDebugRequestHandler { | ||
| private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); | ||
|
|
||
| @Override | ||
| public List<Command> getTargetCommands() { | ||
| return Arrays.asList(Command.DISCONNECT); | ||
| } | ||
|
|
||
| @Override | ||
| public CompletableFuture<Response> handle(Command command, Arguments arguments, Response response, | ||
| IDebugAdapterContext context) { | ||
| destroyDebugSession(command, arguments, response, context); | ||
| destroyResource(context); | ||
| return CompletableFuture.completedFuture(response); | ||
| } | ||
|
|
||
| /** | ||
| * Destroy the resources generated by the debug session. | ||
| * | ||
| * @param context the debug context | ||
| */ | ||
| private void destroyResource(IDebugAdapterContext context) { | ||
| if (shouldDestroyLaunchFiles(context)) { | ||
| destroyLaunchFiles(context); | ||
| } | ||
| } | ||
|
|
||
| private boolean shouldDestroyLaunchFiles(IDebugAdapterContext context) { | ||
| // Delete the temporary launch files must happen after the debuggee process is fully exited, | ||
| // otherwise it throws error saying the file is being used by other process. | ||
| // In Debug mode, the debugger is able to receive VM terminate event. It's sensible to do cleanup. | ||
| // In noDebug mode, if the debuggee is launched internally by the debugger, the debugger knows | ||
| // when the debuggee process exited. Should do cleanup. But if the debuggee is launched in the | ||
| // integrated/external terminal, the debugger lost the contact with the debuggee after it's launched. | ||
| // Have no idea when the debuggee is exited. So ignore the cleanup. | ||
| return context.getLaunchMode() == LaunchMode.DEBUG || context.getDebuggeeProcess() != null; | ||
| } | ||
|
|
||
| private void destroyLaunchFiles(IDebugAdapterContext context) { | ||
| // Sometimes when the debug session is terminated, the debuggee process is not exited immediately. | ||
| // Add retry to delete the temporary launch files. | ||
| int retry = 5; | ||
| while (retry-- > 0) { | ||
| try { | ||
| if (context.getClasspathJar() != null) { | ||
| Files.deleteIfExists(context.getClasspathJar()); | ||
| context.setClasspathJar(null); | ||
| } | ||
|
|
||
| if (context.getArgsfile() != null) { | ||
| Files.deleteIfExists(context.getArgsfile()); | ||
| context.setArgsfile(null); | ||
| } | ||
| } catch (IOException e) { | ||
| // do nothing. | ||
| logger.log(Level.WARNING, "Failed to destory launch files, will retry again."); | ||
| } | ||
|
|
||
| try { | ||
| Thread.sleep(1000); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TimeUnit.SECONDS.sleep(1); |
||
| } catch (InterruptedException e) { | ||
| // do nothing. | ||
| } | ||
| } | ||
| } | ||
|
|
||
| protected abstract void destroyDebugSession(Command command, Arguments arguments, Response response, IDebugAdapterContext context); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,15 +12,24 @@ | |
| 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; | ||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| 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; | ||
|
|
||
| import org.apache.commons.lang3.ArrayUtils; | ||
|
|
@@ -39,6 +48,7 @@ | |
| import com.microsoft.java.debug.core.protocol.Requests.CONSOLE; | ||
| import com.microsoft.java.debug.core.protocol.Requests.Command; | ||
| import com.microsoft.java.debug.core.protocol.Requests.LaunchArguments; | ||
| import com.microsoft.java.debug.core.protocol.Requests.ShortenApproach; | ||
|
|
||
| public class LaunchRequestHandler implements IDebugRequestHandler { | ||
| protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); | ||
|
|
@@ -87,6 +97,62 @@ protected CompletableFuture<Response> handleLaunchCommand(Arguments arguments, R | |
|
|
||
| activeLaunchHandler.preLaunch(launchArguments, context); | ||
|
|
||
| Path tempfile = null; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The body of this function is already long. Please refactor by putting the procedures of generating Jar Manifest and ArgFile into different utility functions. Those functions should be functional without referencing states from other parts. After that the body looks like: if (launchArguments.shortenCommandLine == ShortenApproach.JARMANIFEST) {
Path path = AdapterUtils.generateClasspathJar(...);
context.setClasspathJar(path);
} else if (...) {
} |
||
| // 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(); | ||
| launchArguments.vmArgs += " -cp \"" + tempfile.toAbsolutePath().toString() + "\""; | ||
| launchArguments.classPaths = new String[0]; | ||
| context.setClasspathJar(tempfile); | ||
| } 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()); | ||
| 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; | ||
| } | ||
| } | ||
|
|
||
| return launch(launchArguments, response, context).thenCompose(res -> { | ||
| if (res.success) { | ||
| activeLaunchHandler.postLaunch(launchArguments, context); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.