2009-06-02 13 views
17

Chcę określić nazwę klasy, w której uruchomiłem moją aplikację, tę z metodą main(), w czasie wykonywania, ale jestem w innym wątku, a mój stacktrace nie wraca do oryginalnej klasy.Jak określić główną klasę w środowisku wykonawczym w aplikacji Java z gwintem?

Szukałem właściwości systemu i wszystkiego, co ma do zaoferowania ClassLoader i nie wymyśliłem niczego. Czy ta informacja nie jest dostępna?

Dzięki.

+4

Jestem ciekawy, czy mógłbyś wyjaśnić, dlaczego szukasz tego ...? –

+1

Do ustawiania poziomów dziennika we wspólnym pliku konfiguracyjnym dla wielu aplikacji. –

+1

Zgadzam się z matowym b. Moim zdaniem to nigdy nie powinno być konieczne. Wygląda na to, że twój system stara się być bardziej samoświadomy, niż jest dobry dla siebie. –

Odpowiedz

2

Wymyśliłem to. Czy ktoś może mi powiedzieć, czy ta zmienna środowiskowa będzie zawsze w innych implementacjach java w systemach operacyjnych? To na Oracle JVM daje ciąg jak „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."); 
} 
+1

Dlaczego nie po prostu System.getenv ("JAVA_MAIN_CLASS")? –

+3

Mój miał numer (pid?) Przypięty na końcu klucza. na przykład "JAVA_MAIN_CLASS_13833" Stąd nazwa startsWith(). –

+0

To nie jest dostępne w IBM 1.5.0 JRE, z którego korzystam w wersji –

3

Spróbuj użyć Thread.getAllStackTraces(). Zwraca Mapę śladów stosu ze wszystkich uruchomionych wątków, a nie tylko bieżącego wątku.

+1

To naprawdę nie pomaga, ponieważ wątek, który początkowo był główną klasą, nie musi już działać. –

+5

(Nie pomaga w każdym możliwym przypadku)! = (Nie pomaga w ogóle). –

+1

Zgadzam się i zdałem sobie z tego sprawę po opublikowaniu komentarza. Mimo to dobrze jest mieć świadomość ograniczeń rozwiązania. –

0

Jak o czymś takim:

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

To daje coś

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) 

Główny wątek jest prawdopodobnie nie guarenteed nazywać "main" chociaż - może być lepiej sprawdzić na stosie element śledzenia, który zawiera (main

Edytuj jeśli główny thr ead już wyszedł, to nie jest dobrze!

+0

Próbowałem tego. Nie ma go w moim stosie. To też było moje pierwsze przypuszczenie. –

+0

Dodałem zmianę, publikując ten komentarz. Jeśli wątek już nie istnieje, nie jestem pewien, czy można coś zrobić, aby to zrobić. –

2

Biorąc pod uwagę wyjaśnienia, proponuję za pomocą „Parametryzacja z góry” idiomu. Masz informacje na początek, trzymaj się tego.

Złożyłem RFE 4827318 (sześć lat temu!) Na coś takiego do użytku z biegaczami testowymi.

+0

Rozwiązany w ubiegłym roku, tj. po 10 latach, jako "Nie jest problemem" .Amieszne, czyż nie? – maaartinus

+0

Mój problem jest Nie zawsze kontroluję metodę "główną" ... – rogerdpack

0

Proponuję umieścić tę informację we właściwości systemowej. Zazwyczaj jest to łatwe do zrobienia po uruchomieniu aplikacji ze skryptu.

Jeśli nie możesz tego zrobić, sugeruję ustawić właściwość w metodzie main() każdej aplikacji. Najprostszą metodą byłoby, gdyby każda aplikacja wyprowadzała swoją "główną klasę" ze wspólnej klasy bazowej i uruchamiała tam krok init. Często robię to dla przetwarzania linii poleceń:

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

    // This gets called by Main._main() 
    public void run (String[] args) { 
    } 
} 
6

Zobacz komentarze do linku podanego przez Toma Hawtina. Rozwiązaniem jest tych dniach (Oracle JVM tylko):

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

testowany tylko z Oracle Java 7. Więcej informacji na temat szczególnych przypadkach: http://bugs.java.com/view_bug.do?bug_id=4827318

+0

Podoba mi się ta! Zobacz także http://stackoverflow.com/a/14122039/3245 3 – rogerdpack

+1

W przypadku uruchamiania z słoja podaje nazwę pliku jar, a nie nazwę głównej klasy w słoiku. – sschuberth

2

Wartość środowisko JAVA_MAIN_CLASS nie zawsze jest obecny w zależności od platformy . Jeśli chcesz po prostu otrzymać nazwę „głównym” klasy, który rozpoczął proces Java, można to zrobić:

public static String getMainClassName() 
    { 
    StackTraceElement trace[] = Thread.currentThread().getStackTrace(); 
    if (trace.length > 0) { 
     return trace[trace.length - 1].getClassName(); 
    } 
    return "Unknown"; 
    } 
0

Nawet jeśli wątek z głównego() metoda została zakończona, a ty nie używasz Oracle JVM nadal możesz próbować uzyskać informacje z systemu operacyjnego. Poniższy kod uzyskuje wiersz poleceń używany do uruchamiania maszyny JVM pod Linuksem, ale można napisać wersję dla systemu Windows itp.Następnie można spojrzeć na argumenty do maszyny JVM, aby znaleźć punkt wejścia aplikacji. Może znajdować się bezpośrednio w wierszu poleceń lub możesz szukać klasy głównej: classname w manifeście określonego słoika. Najpierw użyłbym System.getProperty ("sun.java.command"), a przyjaciele w razie potrzeby wycofali się do tego mechanizmu.

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

Oto co używam, w sytuacjach, w których nie kontrolują główne:

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

Innym sposobem, aby dostać główną klasę jest zajrzeć do tej klasy na Thread.getAllStackTraces, więc może go znaleźć nawet w słoikach, działa na każdym 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; 
    } 
} 
Powiązane problemy