Подтвердить что ты не робот

Как определить основной класс во время выполнения в приложении с резьбой java?

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

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

Спасибо.

4b9b3361

Ответ 1

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

Значение среды 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";
  } 

Ответ 6

Как насчет чего-то типа:

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

Изменить, если основной поток вышел, это не хорошо!

Ответ 7

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

Если вы не можете этого сделать, я предлагаю установить свойство в методе 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) {
    }
}

Ответ 8

Даже если поток с методом main() завершился и вы не используете JVM Oracle, вы все равно можете попытаться получить информацию из операционной системы. Код ниже получает командную строку, используемую для запуска JVM под Linux, но вы можете написать версию для Windows и т.д. Затем вы можете посмотреть аргументы JVM, чтобы найти точку входа приложения. Это может быть непосредственно в командной строке, или вы можете искать Main-Class: classname в манифесте указанной банки. Я бы сначала использовал 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;
    }
}`

Ответ 9

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

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

Ответ 10

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