2009-06-02 18 views
17

Je veux déterminer le nom de la classe où mon application a démarré, celle avec la méthode main(), à l'exécution, mais je suis dans un autre thread et mon stacktrace ne retourne pas à la classe d'origine.Comment déterminer la classe principale au moment de l'exécution dans une application java threadée?

J'ai recherché les propriétés du système et tout ce que ClassLoader a à offrir et ne rien trouver. Cette information n'est-elle pas disponible?

Merci.

+4

Je suis curieux de savoir si vous pourriez expliquer pourquoi vous cherchez ce ..? –

+1

Pour définir les niveaux de consignation dans un fichier de configuration commun pour plusieurs applications. –

+1

Je suis d'accord avec mat b. À mon avis, cela ne devrait jamais être nécessaire. Il semble que votre système essaie d'être plus conscient de lui-même que ce qui est bon pour lui-même. –

Répondre

2

Je l'ai compris. Quelqu'un peut-il me dire si cette variable d'environnement sera toujours dans d'autres implémentations java à travers les systèmes d'exploitation? Cette machine virtuelle Java sur les rendements Oracle une chaîne comme « 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

Pourquoi pas simplement System.getenv ("JAVA_MAIN_CLASS")? –

+3

Le mien avait un numéro (pid?) Collé sur la fin de la clé. par exemple. "JAVA_MAIN_CLASS_13833" D'où le startsWith(). –

+0

Ce n'est pas disponible sur IBM 1.5.0 JRE, que j'utilise –

3

Essayez d'utiliser Thread.getAllStackTraces(). Il renvoie une carte des traces de la pile de tous les threads en cours d'exécution, pas seulement celle en cours.

+1

Cela n'aide pas vraiment, car le thread qui a initialement exécuté la classe principale n'a plus besoin de fonctionner. –

+5

(N'aide pas dans tous les cas imaginables)! = (N'aide pas du tout). –

+1

Je suis d'accord et s'est rendu compte qu'après avoir posté le commentaire. Pourtant, il est bon d'être conscient des limites d'une solution. –

0

Que diriez-vous quelque chose comme:

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

Cela vous donnera quelque chose comme

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) 

Le fil conducteur est probablement pas guarenteed être appelé "main" bien - peut-être préférable de vérifier une pile élément trace qui contient (main

Modifier si le fichier principal ead est sorti, ce n'est pas bon!

+0

J'ai essayé ça. Ce n'est pas dans ma pile. C'était aussi ma première estimation. –

+0

J'ai ajouté une modification lorsque vous avez posté ce commentaire. Si le thread n'existe plus, je ne suis pas sûr qu'il y ait beaucoup à faire –

2

Compte tenu de la précision, je suggère d'utiliser l'idiome « ​​paramétrisation d'en haut ». Vous avez l'information pour commencer, gardez le.

J'ai fait un RFE 4827318 (il y a six ans!) Pour quelque chose comme ça pour une utilisation avec des coureurs de test.

+0

Résolu l'année dernière, c'est-à-dire après 10 ans, comme "Pas un problème" .Pas drôle, n'est-ce pas? – maaartinus

+0

Mon problème est Je ne contrôle pas toujours la méthode 'main' ... – rogerdpack

0

Je suggère de mettre cette information dans une propriété système. C'est généralement simple à faire lorsque vous démarrez votre application à partir d'un script.

Si vous ne pouvez pas faire cela, je suggère de définir la propriété dans la méthode main() de chaque application. La manière la plus simple serait de faire en sorte que chaque application tire sa classe principale d'une classe de base commune et lance une étape d'initialisation. Je le fais souvent pour le traitement en ligne de commande:

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

Voir les commentaires sur le lien donné par Tom Hawtin. Une solution est ces jours-ci est (JVM Oracle uniquement):

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

Testé uniquement avec Oracle Java 7. Plus d'informations sur des cas particuliers: http://bugs.java.com/view_bug.do?bug_id=4827318

+0

J'aime celui-ci! Voir aussi http://stackoverflow.com/a/14122039/3245 3 – rogerdpack

+1

Si cette commande est exécutée à partir d'un fichier jar, elle donne le nom du fichier jar et non le nom de la classe principale dans le fichier jar. – sschuberth

2

La valeur de l'environnement JAVA_MAIN_CLASS n'est pas toujours présent en fonction de la plate-forme . Si vous voulez juste pour obtenir un nom de la classe « principale » qui a commencé votre processus Java, vous pouvez le faire:

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

Même si le fil avec la méthode principale() a pris fin et que vous n'êtes pas en utilisant la Oracle JVM vous pouvez toujours tenter d'obtenir les informations du système d'exploitation. Le code ci-dessous obtient la ligne de commande utilisée pour démarrer la JVM sous Linux mais vous pouvez écrire une version pour Windows, etc.Vous pouvez ensuite examiner les arguments de la JVM pour trouver le point d'entrée de l'application. Il se peut que ce soit directement sur la ligne de commande ou que vous ayez recherché Main-Class: classname dans le manifeste du fichier jar spécifié. J'utiliserais d'abord System.getProperty ("sun.java.command") et mes amis ne retomberaient sur ce mécanisme que si nécessaire.

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

Voici ce que j'utilise, pour les situations où vous ne contrôlez pas le principal:

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

Une autre façon d'obtenir la classe principale est de chercher cette classe sur Thread.getAllStackTraces, vous pourrait le trouver même à l'intérieur des pots, il fonctionne sur n'importe quel 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; 
    } 
}