I've written a program that outputs a usage hint. It currently echos back the path to the main jar
file as was originally entered on the command line.
Usage: java -jar path/to/MyJar.jar <params> ...
For completeness, I'd like to make sure that the java
bit is echoed back as well, as there are various ways to access java
, (beyond just the word java
, and shorter than the canonical path to /us/opt/java-1.8.0-u123/bin/java
)
Usage: /us/opt/java7/bin/java -jar MyJar.jar <params> ...
Usage: ./bin/java -jar MyJar.jar <params> ...
Usage: java -jar MyJar.jar <params> ...
# whatever the user typed in
How can I determine what command-line was used to evoke the JVM?
I would like the original command-line value, prior to evaluating symbolic links.
I'm not using System.getProperty("java.home")
because it has no respect for the original command-line value, just the final 'canonical' location of the JVM. (Having a usage note like Usage: /us/opt/java-1.8.0-u123/jre/bin/java -jar ...
would be rather verbose,
especially when using simple java
on the command line.)
Is determining the command-line location of java
possible using pure Java code?
(i.e. not using a wrapper script in bash
)
Use following command
jps -mlvV
This should print everything about running java processes.
Where as ps -e
should give you executable path.
Following is pure Java solution, it doesn't print the actual command used to execute the application but it produces a command which will have the same effect.
import java.io.File;
import java.lang.management.ManagementFactory;
import java.util.List;
import java.util.Map.Entry;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
System.out.println("Printing command");
createCommand();
}
public static void createCommand() {
try {
String jvm = getJvmExecutable();
String mainClassName = findMainClass();
String processDir = System.getProperty("user.dir");
String arguments = getArguments();
String classpath = ManagementFactory.getRuntimeMXBean().getClassPath();
String command = String.format("cd %s & %s %s -classpath %s %s",processDir, jvm, arguments, classpath, mainClassName);
System.out.println(command);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static String getJvmExecutable() {
String jvm = "";
jvm = System.getProperty("java.home");
jvm += File.separator + "bin" + File.separator + "java";
jvm = '"' + jvm + '"';
return jvm;
}
private static String getArguments() {
List<String> argsList = ManagementFactory.getRuntimeMXBean().getInputArguments();
String args = argsList.stream().collect(Collectors.joining(" "));
return args;
}
public static String findMainClass() throws ClassNotFoundException{
for (Entry<Thread, StackTraceElement[]> entry : Thread.getAllStackTraces().entrySet()) {
Thread thread = entry.getKey();
if (thread.getThreadGroup() != null && thread.getThreadGroup().getName().equals("main")) {
for (StackTraceElement stackTraceElement : entry.getValue()) {
if (stackTraceElement.getMethodName().equals("main")) {
try {
Class<?> c = Class.forName(stackTraceElement.getClassName());
Class[] argTypes = new Class[] { String[].class };
//This will throw NoSuchMethodException in case of fake main methods
c.getDeclaredMethod("main", argTypes);
return stackTraceElement.getClassName();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
}
}
return null;
}
}
Note: It will work with plain java projects or normal JARS, however it will not work with special classloading like WAR, OSGI or Spring Boot's classloading.
I used the findMainClass()
method from this answer https://stackoverflow.com/a/8275751/5343269
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With