Как определить основной класс во время выполнения в многопоточном Java-приложении? - PullRequest
17 голосов
/ 02 июня 2009

Я хочу определить имя класса, с которого запущено мое приложение, то есть с методом main (), во время выполнения, но я в другом потоке, и моя трассировка стека не возвращается полностью к исходному классу.

Я искал свойства системы и все, что может предложить ClassLoader, и ничего не нашел. Эта информация просто недоступна?

Спасибо.

Ответы [ 10 ]

6 голосов
/ 07 ноября 2014

Смотрите комментарии по ссылке, предоставленной Томом Хотином. Решение в эти дни (только Oracle JVM):

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

Протестировано только с Oracle Java 7. Дополнительная информация о специальных случаях: http://bugs.java.com/view_bug.do?bug_id=4827318

4 голосов
/ 02 июня 2009

Попробуйте использовать Thread.getAllStackTraces () . Он возвращает карту трассировки стека от всех запущенных потоков, а не только от текущего.

3 голосов
/ 30 апреля 2016

Значение среды JAVA_MAIN_CLASS не всегда присутствует в зависимости от платформы. Если вы просто хотите получить имя «основного» класса, который запустил ваш Java-процесс, вы можете сделать это:

  public static String getMainClassName()
  {
    StackTraceElement trace[] = Thread.currentThread().getStackTrace();
    if (trace.length > 0) {
      return trace[trace.length - 1].getClassName();
    }
    return "Unknown";
  } 
2 голосов
/ 02 июня 2009

Учитывая разъяснение, я предлагаю использовать идиому «Параметризация сверху». У вас есть информация для начала, держите ее.

Я положил в RFE 4827318 (шесть лет назад!) Что-то подобное для использования с бегунами.

2 голосов
/ 02 июня 2009

Я понял это. Может кто-нибудь сказать мне, будет ли эта переменная среды всегда присутствовать в других реализациях Java в операционных системах? Это в Oracle JVM дает строку типа "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.");
}
0 голосов
/ 11 января 2018

Другой способ получить основной класс - найти этот класс в Thread.getAllStackTraces, чтобы вы могли найти его даже внутри jar-файлов, он работает на любом 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;
    }
}
0 голосов
/ 23 июля 2017

Вот что я использую для ситуаций, когда вы не контролируете главное:

public static Class<?> getMainClass() {
  // find the class that called us, and use their "target/classes"
  final Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
  for (Entry<Thread, StackTraceElement[]> trace : traces.entrySet()) {
    if ("main".equals(trace.getKey().getName())) {
      // Using a thread named main is best...
      final StackTraceElement[] els = trace.getValue();
      int i = els.length - 1;
      StackTraceElement best = els[--i];
      String cls = best.getClassName();
      while (i > 0 && isSystemClass(cls)) {
        // if the main class is likely an ide,
        // then we should look higher...
        while (i-- > 0) {
          if ("main".equals(els[i].getMethodName())) {
            best = els[i];
            cls = best.getClassName();
            break;
          }
        }
      }
      if (isSystemClass(cls)) {
        i = els.length - 1;
        best = els[i];
        while (isSystemClass(cls) && i --> 0) {
          best = els[i];
          cls = best.getClassName();
        }
      }
      try {
        Class mainClass = Class.forName(best.getClassName());
        return mainClass;
      } catch (ClassNotFoundException e) {
        throw X_Util.rethrow(e);
      }
    }
  }
  return null;
}

private static boolean isSystemClass(String cls) {
  return cls.startsWith("java.") ||
      cls.startsWith("sun.") ||
      cls.startsWith("org.apache.maven.") ||
      cls.contains(".intellij.") ||
      cls.startsWith("org.junit") ||
      cls.startsWith("junit.") ||
      cls.contains(".eclipse") ||
      cls.contains("netbeans");
}
0 голосов
/ 12 января 2017

Даже если поток с методом main () завершен, и вы не используете JVM Oracle, вы все равно можете попытаться получить информацию из операционной системы. Приведенный ниже код получает командную строку, используемую для запуска JVM под Linux, но вы можете написать версию для Windows и т. Д. Затем вы можете просмотреть аргументы JVM, чтобы найти точку входа в приложение. Это может быть непосредственно в командной строке, или вы можете искать Main-Class: classname в манифесте указанного jar. Сначала я бы использовал System.getProperty ("sun.java.command"), а друзья возвращались к этому механизму только при необходимости.

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;
    }
}`
0 голосов
/ 02 июня 2009

Предлагаю поместить эту информацию в системное свойство. Обычно это легко сделать, когда вы запускаете приложение из скрипта.

Если вы не можете этого сделать, я предлагаю установить свойство в методе main () каждого приложения. Самый простой способ - получить каждое приложение, извлекающее его «основной класс» из общего базового класса, и выполнить там шаг инициализации. Я часто делаю это для обработки командной строки:

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

    // This gets called by Main._main()
    public void run (String[] args) {
    }
}
0 голосов
/ 02 июня 2009

Как насчет чего-то вроде:

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);
        }
    }
}

Это даст вам что-то вроде

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)

Основной поток, вероятно, не гарантируется для вызова "main", хотя - может быть лучше проверить элемент трассировки стека, который содержит (main

Редактировать Если основной поток завершился, это не хорошо!

...