How to determine main class at runtime in threaded

2019-02-08 07:51发布

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 way back to the original class.

I've searched System properties and everything that ClassLoader has to offer and come up with nothing. Is this information just not available?

Thanks.

10条回答
贼婆χ
2楼-- · 2019-02-08 08:04

I suggest to put this information into a system property. This is usually simple to do when you start your application from a script.

If you can't do that, I suggest to set the property in the main() method of every application. The most simple way here would be to have every app derive it's "main class" from a common base class and run an init step in there. I often do this for command line processing:

public class Demo extends Main {
    main(String[] args) {
        Main._main(new Demo (), args);
    }

    // This gets called by Main._main()
    public void run (String[] args) {
    }
}
查看更多
Luminary・发光体
3楼-- · 2019-02-08 08:05

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;
    }
}
查看更多
beautiful°
4楼-- · 2019-02-08 08:11

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";
  } 
查看更多
forever°为你锁心
5楼-- · 2019-02-08 08:13

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.");
}
查看更多
叼着烟拽天下
6楼-- · 2019-02-08 08:15

See the comments on link given by Tom Hawtin. A solution is these days is (Oracle JVM only):

public static String getMainClassAndArgs() {
    return System.getProperty("sun.java.command"); // like "org.x.y.Main arg1 arg2"
}

Tested only with Oracle Java 7. More information about special cases: http://bugs.java.com/view_bug.do?bug_id=4827318

查看更多
女痞
7楼-- · 2019-02-08 08:15

How about something like:

Map<Thread,StackTraceElement[]> stackTraceMap = Thread.getAllStackTraces();
for (Thread t : stackTraceMap.keySet())
{
    if ("main".equals(t.getName()))
    {
        StackTraceElement[] mainStackTrace = stackTraceMap.get(t);
        for (StackTraceElement element : mainStackTrace)
        {
            System.out.println(element);
        }
    }
}

This will give you something like

java.lang.Object.wait(Native Method)
java.lang.Object.wait(Object.java:231)
java.lang.Thread.join(Thread.java:680)
com.mypackage.Runner.main(Runner.java:10)

The main thread is probably not guarenteed to be called "main" though - might be better to check for a stack trace element that contains (main

Edit if the main thread has exited, this is no good!

查看更多
登录 后发表回答