I want to determine the class name where my application started, the one with the main() method, at runtime, but I\'m in another thread and my stacktrace doesn\'t go all the
The JAVA_MAIN_CLASS environment value isn't always present depending on the platform. If you just want to get a name of the "main" class that started your Java process, you can do this:
public static String getMainClassName()
{
StackTraceElement trace[] = Thread.currentThread().getStackTrace();
if (trace.length > 0) {
return trace[trace.length - 1].getClassName();
}
return "Unknown";
}
Even if the thread with the main() method has terminated and you are not using the Oracle JVM you can still attempt to get the information from the operating system. The code below obtains the command line used to start the JVM under Linux but you could write a version for Windows, etc. You could then look at the arguments to the JVM to find the application entry point. It might be directly on the command line or you might have look for Main-Class: classname in the manifest of the specified jar. I would first use System.getProperty("sun.java.command") and friends only falling back to this mechanism if necessary.
public final static long getSelfPid() {
// Java 9 only
// return ProcessHandle.current().getPid();
try {
return Long.parseLong(new File("/proc/self").getCanonicalFile().getName());
} catch( Exception e ) {
return -1;
}
}
public final static String getJVMCommandLine() {
try {
// Java 9 only
// long pid = ProcessHandle.current().getPid();
long pid = getSelfPid();
byte[] encoded = Files.readAllBytes(Paths.get("/proc/"+pid+"/cmdline"));
// assume ISO_8859_1, but could look in /proc/<pid>/environ for LANG or something I suppose
String commandLine = new String( encoded, StandardCharsets.ISO_8859_1 );
String modifiedCommandLine = commandLine.replace((char)0, ' ').trim();
return modifiedCommandLine;
} catch( Exception e ) {
return null;
}
}`
Try using Thread.getAllStackTraces(). It returns a Map of the stack traces from all running threads, not just the current one.
Given the clarification, I suggest using the "Parameterisation from Above" idiom. You have the information to start with, keep hold of it.
I did put in an RFE 4827318 (six years ago!) for something like this for use with test runners.
Another way to get main class is look for that class on Thread.getAllStackTraces, so you could find it even inside jars, it works on any SDK (Open, Oracle...):
private static Class<?> mainClass = null;
public static Class<?> getMainClass()
{
if (mainClass == null)
{
Map<Thread, StackTraceElement[]> threadSet = Thread.getAllStackTraces();
for (Map.Entry<Thread, StackTraceElement[]> entry : threadSet.entrySet())
{
for (StackTraceElement stack : entry.getValue())
{
try
{
String stackClass = stack.getClassName();
if (stackClass != null && stackClass.indexOf("$") > 0)
{
stackClass = stackClass.substring(0, stackClass.lastIndexOf("$"));
}
Class<?> instance = Class.forName(stackClass);
Method method = instance.getDeclaredMethod("main", new Class[]
{
String[].class
});
if (Modifier.isStatic(method.getModifiers()))
{
mainClass = instance;
break;
}
}
catch (Exception ex)
{
}
}
}
return mainClass;
}
}
I figured it out. Can anyone tell me if this environment variable will always be around in other java implementations across operating systems? This on Oracle JVM yields a String like "org.x.y.ClassName"
public static String getMainClassName() {
for (final Map.Entry<String, String> entry : System.getenv().entrySet())
if (entry.getKey().startsWith("JAVA_MAIN_CLASS")) // like JAVA_MAIN_CLASS_13328
return entry.getValue();
throw new IllegalStateException("Cannot determine main class.");
}