実行時に、アプリケーションが開始されたクラス名 (main() メソッドを含むクラス名) を特定したいのですが、別のスレッドにいて、スタック トレースが元のクラスにまでさかのぼりません。
システム プロパティと、ClassLoader が提供するすべてのものを検索しましたが、何も見つかりませんでした。この情報は入手できないだけですか?
ありがとう。
実行時に、アプリケーションが開始されたクラス名 (main() メソッドを含むクラス名) を特定したいのですが、別のスレッドにいて、スタック トレースが元のクラスにまでさかのぼりません。
システム プロパティと、ClassLoader が提供するすべてのものを検索しましたが、何も見つかりませんでした。この情報は入手できないだけですか?
ありがとう。
Tom Hawtin によるリンクのコメントを参照してください。最近の解決策は次のとおりです(Oracle JVMのみ):
public static String getMainClassAndArgs() {
return System.getProperty("sun.java.command"); // like "org.x.y.Main arg1 arg2"
}
Oracle Java 7 でのみテスト済み。特殊なケースに関する詳細情報: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4827318
私はそれを考え出した。この環境変数がオペレーティングシステム全体の他のJava実装に常に存在するかどうか誰かに教えてもらえますか?これは、Oracle JVMで、「org.xyClassName」のような文字列を生成します。
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.");
}
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";
}
Thread.getAllStackTraces()を使用してみてください。現在のスレッドだけでなく、実行中のすべてのスレッドからのスタック トレースの Map を返します。
明確にするために、「上からのパラメーター化」イディオムを使用することをお勧めします。あなたは最初に情報を持っています、それを保持してください。
テストランナーで使用するために、このようなもののためにRFE 4827318(6年前!)を入れました。
main() メソッドを含むスレッドが終了し、Oracle JVM を使用していない場合でも、オペレーティング システムから情報を取得しようとすることができます。以下のコードは、Linux で JVM を起動するために使用されるコマンド ラインを取得しますが、Windows など用のバージョンを作成することもできます。次に、JVM への引数を調べて、アプリケーションのエントリ ポイントを見つけることができます。コマンドラインで直接指定するか、指定した jar のマニフェストで 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;
}
}`
この情報をシステム プロパティに入れることをお勧めします。これは通常、スクリプトからアプリケーションを起動する場合に簡単に実行できます。
それができない場合は、すべてのアプリケーションの main() メソッドでプロパティを設定することをお勧めします。ここでの最も簡単な方法は、すべてのアプリが共通の基本クラスから「メイン クラス」を派生させ、そこで init ステップを実行することです。コマンドライン処理のためにこれを行うことがよくあります。
public class Demo extends Main {
main(String[] args) {
Main._main(new Demo (), args);
}
// This gets called by Main._main()
public void run (String[] args) {
}
}
次のようなものはどうですか:
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
メインスレッドが終了している場合は編集してください。これはダメです!