29 import java.io.UncheckedIOException; |
29 import java.io.UncheckedIOException; |
30 import java.nio.file.Files; |
30 import java.nio.file.Files; |
31 import java.nio.file.Paths; |
31 import java.nio.file.Paths; |
32 import java.util.jar.Attributes; |
32 import java.util.jar.Attributes; |
33 import java.util.jar.JarFile; |
33 import java.util.jar.JarFile; |
34 import java.util.jar.Manifest; |
|
35 import java.util.stream.Stream; |
34 import java.util.stream.Stream; |
36 |
35 |
37 /** |
36 /** |
38 * A helper class that retrieves the main class name for |
37 * A helper class that retrieves the main class name for |
39 * a running Java process using the proc filesystem (procfs) |
38 * a running Java process using the proc filesystem (procfs) |
48 return INSTANCE; |
47 return INSTANCE; |
49 } |
48 } |
50 |
49 |
51 /** |
50 /** |
52 * Gets the main class name for the given Java process by parsing the |
51 * Gets the main class name for the given Java process by parsing the |
53 * process command line. |
52 * process command line. If the application was started with the <em>-jar</em> |
|
53 * option this method returns the name of the jar file. If the application |
|
54 * was started with <em>-m</em> or <em>--module</em> option, the method returns |
|
55 * the module name and the main class name. |
54 * @param pid - process ID (pid) |
56 * @param pid - process ID (pid) |
55 * @return main class name or null if the process no longer exists or |
57 * @return the main class name or null if the process no longer exists or |
56 * was started with a native launcher (e.g. jcmd etc) |
58 * was started with a native launcher (e.g. jcmd etc) |
57 */ |
59 */ |
58 |
60 |
59 public String getMainClass(String pid) { |
61 public String getMainClass(String pid) { |
60 String cmdLine = getCommandLine(pid); |
62 String cmdLine = getCommandLine(pid); |
79 // Skip the process if it is not started with java launcher |
81 // Skip the process if it is not started with java launcher |
80 return null; |
82 return null; |
81 } |
83 } |
82 } |
84 } |
83 |
85 |
84 // If -jar option is used then read the main class name from the manifest file. |
86 // To be consistent with the behavior on other platforms, if -jar, -m, or --module |
85 // Otherwise, the main class name is either specified in -m or --module options or it |
87 // options are used then just return the value (the path to the jar file or module |
86 // is the first part that is not a Java option (doesn't start with '-' and is not a |
88 // name with a main class). Otherwise, the main class name is the first part that |
87 // classpath or a module path). |
89 // is not a Java option (doesn't start with '-' and is not a classpath or a module |
|
90 // path). |
88 |
91 |
89 for (int i = 1; i < parts.length && mainClass == null; i++) { |
92 for (int i = 1; i < parts.length && mainClass == null; i++) { |
90 if (i < parts.length - 1) { |
93 if (i < parts.length - 1) { |
91 // Check if the module is executed with explicitly specified main class |
94 if (parts[i].equals("-m") || parts[i].equals("--module") || parts[i].equals("-jar")) { |
92 if ((parts[i].equals("-m") || parts[i].equals("--module"))) { |
95 return parts[i + 1]; |
93 return getMainClassFromModuleArg(parts[i + 1]); |
|
94 } |
|
95 // Check if the main class needs to be read from the manifest.mf in a JAR file |
|
96 if (parts[i].equals("-jar")) { |
|
97 return getMainClassFromJar(parts[i + 1], pid); |
|
98 } |
96 } |
99 } |
97 } |
100 // If this is a classpath or a module path option then skip the next part |
98 // If this is a classpath or a module path option then skip the next part |
101 // (the classpath or the module path itself) |
99 // (the classpath or the module path itself) |
102 if (parts[i].equals("-cp") || parts[i].equals("-classpath") || parts[i].equals("--class-path") || |
100 if (parts[i].equals("-cp") || parts[i].equals("-classpath") || parts[i].equals("--class-path") || |
112 } |
110 } |
113 return mainClass; |
111 return mainClass; |
114 |
112 |
115 } |
113 } |
116 |
114 |
117 private String getMainClassFromModuleArg(String moduleArg) { |
|
118 int pos = moduleArg.lastIndexOf("/"); |
|
119 return (pos > 0 && pos < moduleArg.length()-1) ? moduleArg.substring(pos + 1) : null; |
|
120 } |
|
121 |
|
122 private String getMainClassFromJar(String jar, String pid) { |
|
123 if (!jar.startsWith("/")) { |
|
124 String cwd = getCurrentWorkingDir(pid); |
|
125 if (cwd != null) { |
|
126 jar = cwd + "/" + jar; |
|
127 } |
|
128 } |
|
129 try (JarFile jarFile = new JarFile(jar)) { |
|
130 Manifest mf = jarFile.getManifest(); |
|
131 if (mf != null) { |
|
132 Attributes mainAttributes = mf.getMainAttributes(); |
|
133 return mainAttributes.getValue("Main-Class"); |
|
134 } |
|
135 } catch (IOException e) { |
|
136 return null; |
|
137 } |
|
138 return null; |
|
139 } |
|
140 |
|
141 private static String getCurrentWorkingDir(String pid) { |
|
142 return ("/proc/" + pid + "/cwd"); |
|
143 } |
|
144 |
|
145 private static String getCommandLine(String pid) { |
115 private static String getCommandLine(String pid) { |
146 try (Stream<String> lines = |
116 try (Stream<String> lines = |
147 Files.lines(Paths.get("/proc/" + pid + "/cmdline"))) { |
117 Files.lines(Paths.get("/proc/" + pid + "/cmdline"))) { |
148 return lines.map(x -> x.replaceAll("\0", " ")).findFirst().orElse(null); |
118 return lines.map(x -> x.replaceAll("\0", " ")).findFirst().orElse(null); |
149 } catch (IOException | UncheckedIOException e) { |
119 } catch (IOException | UncheckedIOException e) { |