From 7d2074538ab192af5e5a4d8a5cecd13a6df1ab93 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 25 Jan 2021 14:27:40 +0800 Subject: [PATCH 1/2] Avoid using the same temporary launch file when triggering multiple debug sessions in parallel --- .../java/debug/core/adapter/AdapterUtils.java | 111 +----------- .../adapter/handler/LaunchRequestHandler.java | 8 +- .../core/adapter/handler/LaunchUtils.java | 166 ++++++++++++++++++ 3 files changed, 172 insertions(+), 113 deletions(-) create mode 100644 com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java index 132d702d9..74d3fb292 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2017-2019 Microsoft Corporation and others. +* Copyright (c) 2017-2021 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 @@ -12,10 +12,7 @@ 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.math.BigInteger; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; @@ -28,17 +25,11 @@ 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; @@ -46,8 +37,6 @@ import com.microsoft.java.debug.core.protocol.Responses; import com.microsoft.java.debug.core.protocol.Types; -import sun.security.action.GetPropertyAction; - public class AdapterUtils { private static final String OS_NAME = System.getProperty("os.name", "").toLowerCase(); private static final Pattern ENCLOSING_CLASS_REGEX = Pattern.compile("^([^\\$]*)"); @@ -301,102 +290,4 @@ 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 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 - String classpathValue = String.join(" ", classpathUrls); - attributes.put(Attributes.Name.CLASS_PATH, classpathValue); - Path tempfile = createTempFile("cp_" + getMd5(classpathValue), ".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)) { - argfile = "-classpath \"" + String.join(File.pathSeparator, classPaths) + "\""; - } - - if (ArrayUtils.isNotEmpty(modulePaths)) { - argfile = " --module-path \"" + String.join(File.pathSeparator, modulePaths) + "\""; - } - - argfile = argfile.replace("\\", "\\\\"); - Path tempfile = createTempFile("cp_" + getMd5(argfile), ".argfile"); - Files.write(tempfile, argfile.getBytes()); - - return tempfile; - } - - private static Path tmpdir = null; - - private static synchronized Path getTmpDir() throws IOException { - if (tmpdir == null) { - try { - tmpdir = Paths.get(java.security.AccessController.doPrivileged(new GetPropertyAction("java.io.tmpdir"))); - } catch (NullPointerException | InvalidPathException e) { - Path tmpfile = Files.createTempFile("", ".tmp"); - tmpdir = tmpfile.getParent(); - try { - Files.deleteIfExists(tmpfile); - } catch (Exception ex) { - // do nothing - } - } - } - - return tmpdir; - } - - private static Path createTempFile(String baseName, String suffix) throws IOException { - // loop until the temp file can be created - SecurityManager sm = System.getSecurityManager(); - for (int i = 0; ; i++) { - Path tempFile = getTmpDir().resolve(baseName + (i == 0 ? "" : i) + suffix); - try { - // delete the old temp file - Files.deleteIfExists(tempFile); - } catch (Exception e) { - // do nothing - } - - if (!Files.exists(tempFile)) { - return Files.createFile(tempFile); - } - } - } - - private static String getMd5(String input) { - try { - MessageDigest md = MessageDigest.getInstance("MD5"); - byte[] messageDigest = md.digest(input.getBytes()); - BigInteger md5 = new BigInteger(1, messageDigest); - return md5.toString(Character.MAX_RADIX); - } catch (NoSuchAlgorithmException e) { - return Integer.toString(input.hashCode(), Character.MAX_RADIX); - } - } } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index 54c4127c8..7d76e1ffd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2018 Microsoft Corporation and others. +* Copyright (c) 2018-2021 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 @@ -115,7 +115,7 @@ protected CompletableFuture handleLaunchCommand(Arguments arguments, R if (launchArguments.shortenCommandLine == ShortenApproach.JARMANIFEST) { if (ArrayUtils.isNotEmpty(launchArguments.classPaths)) { try { - Path tempfile = AdapterUtils.generateClasspathJar(launchArguments.classPaths); + Path tempfile = LaunchUtils.generateClasspathJar(launchArguments.classPaths); launchArguments.vmArgs += " -cp \"" + tempfile.toAbsolutePath().toString() + "\""; launchArguments.classPaths = new String[0]; context.setClasspathJar(tempfile); @@ -129,7 +129,7 @@ protected CompletableFuture handleLaunchCommand(Arguments arguments, R } } else if (launchArguments.shortenCommandLine == ShortenApproach.ARGFILE) { try { - Path tempfile = AdapterUtils.generateArgfile(launchArguments.classPaths, launchArguments.modulePaths); + Path tempfile = LaunchUtils.generateArgfile(launchArguments.classPaths, launchArguments.modulePaths); launchArguments.vmArgs += " \"@" + tempfile.toAbsolutePath().toString() + "\""; launchArguments.classPaths = new String[0]; launchArguments.modulePaths = new String[0]; @@ -140,6 +140,8 @@ protected CompletableFuture handleLaunchCommand(Arguments arguments, R } return launch(launchArguments, response, context).thenCompose(res -> { + LaunchUtils.releaseTempLaunchFile(context.getClasspathJar()); + LaunchUtils.releaseTempLaunchFile(context.getArgsfile()); if (res.success) { activeLaunchHandler.postLaunch(launchArguments, context); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java new file mode 100644 index 000000000..0067f3280 --- /dev/null +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -0,0 +1,166 @@ +/******************************************************************************* +* Copyright (c) 2021 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.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +import com.microsoft.java.debug.core.adapter.AdapterUtils; + +import org.apache.commons.lang3.ArrayUtils; + +import sun.security.action.GetPropertyAction; + +public class LaunchUtils { + private static Set tempFilesInUse = new HashSet<>(); + + /** + * 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 synchronized static Path generateClasspathJar(String[] classPaths) throws IOException { + List 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 + String classpathValue = String.join(" ", classpathUrls); + attributes.put(Attributes.Name.CLASS_PATH, classpathValue); + String baseName = "cp_" + getMd5(classpathValue); + cleanupTempFiles(baseName, ".jar"); + Path tempfile = createTempFile(baseName, ".jar"); + JarOutputStream jar = new JarOutputStream(new FileOutputStream(tempfile.toFile()), manifest); + jar.close(); + lockTempLaunchFile(tempfile); + + 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 synchronized static Path generateArgfile(String[] classPaths, String[] modulePaths) throws IOException { + String argfile = ""; + if (ArrayUtils.isNotEmpty(classPaths)) { + argfile = "-classpath \"" + String.join(File.pathSeparator, classPaths) + "\""; + } + + if (ArrayUtils.isNotEmpty(modulePaths)) { + argfile = " --module-path \"" + String.join(File.pathSeparator, modulePaths) + "\""; + } + + argfile = argfile.replace("\\", "\\\\"); + String baseName = "cp_" + getMd5(argfile); + cleanupTempFiles(baseName, ".argfile"); + Path tempfile = createTempFile(baseName, ".argfile"); + Files.write(tempfile, argfile.getBytes()); + lockTempLaunchFile(tempfile); + + return tempfile; + } + + public static void lockTempLaunchFile(Path tempFile) { + if (tempFile != null) { + tempFilesInUse.add(tempFile); + } + } + + public static void releaseTempLaunchFile(Path tempFile) { + if (tempFile != null) { + tempFilesInUse.remove(tempFile); + } + } + + private static Path tmpdir = null; + + private static synchronized Path getTmpDir() throws IOException { + if (tmpdir == null) { + try { + tmpdir = Paths.get(java.security.AccessController.doPrivileged(new GetPropertyAction("java.io.tmpdir"))); + } catch (NullPointerException | InvalidPathException e) { + Path tmpfile = Files.createTempFile("", ".tmp"); + tmpdir = tmpfile.getParent(); + try { + Files.deleteIfExists(tmpfile); + } catch (Exception ex) { + // do nothing + } + } + } + + return tmpdir; + } + + private static void cleanupTempFiles(String baseName, String suffix) throws IOException { + for (int i = 0; ; i++) { + Path tempFile = getTmpDir().resolve(baseName + (i == 0 ? "" : i) + suffix); + if (tempFilesInUse.contains(tempFile)) { + continue; + } else if (!Files.exists(tempFile)) { + break; + } else { + try { + // delete the old temp file + Files.deleteIfExists(tempFile); + } catch (Exception e) { + // do nothing + } + } + } + } + + private static Path createTempFile(String baseName, String suffix) throws IOException { + // loop until the temp file can be created + for (int i = 0; ; i++) { + Path tempFile = getTmpDir().resolve(baseName + (i == 0 ? "" : i) + suffix); + if (!Files.exists(tempFile)) { + return Files.createFile(tempFile); + } + } + } + + private static String getMd5(String input) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] messageDigest = md.digest(input.getBytes()); + BigInteger md5 = new BigInteger(1, messageDigest); + return md5.toString(Character.MAX_RADIX); + } catch (NoSuchAlgorithmException e) { + return Integer.toString(input.hashCode(), Character.MAX_RADIX); + } + } +} From 577a9d6bc34233d42945b95f6437c4341f26f055 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Mon, 25 Jan 2021 14:50:29 +0800 Subject: [PATCH 2/2] Fix checkstyle --- .../java/debug/core/adapter/handler/LaunchUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java index 0067f3280..be7f95126 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchUtils.java @@ -44,7 +44,7 @@ public class LaunchUtils { * @return the file path of the generate classpath.jar * @throws IOException Some errors occur during generating the classpath.jar */ - public synchronized static Path generateClasspathJar(String[] classPaths) throws IOException { + public static synchronized Path generateClasspathJar(String[] classPaths) throws IOException { List classpathUrls = new ArrayList<>(); for (String classpath : classPaths) { classpathUrls.add(AdapterUtils.toUrl(classpath)); @@ -73,7 +73,7 @@ public synchronized static Path generateClasspathJar(String[] classPaths) throws * @return the file path of the generated argfile * @throws IOException Some errors occur during generating the argfile */ - public synchronized static Path generateArgfile(String[] classPaths, String[] modulePaths) throws IOException { + public static synchronized Path generateArgfile(String[] classPaths, String[] modulePaths) throws IOException { String argfile = ""; if (ArrayUtils.isNotEmpty(classPaths)) { argfile = "-classpath \"" + String.join(File.pathSeparator, classPaths) + "\"";