JVMが起動されたメインクラス名を取得する

以前に同様のメソッドをスタックトレースから取得する実装にしていたのだが、現在のスレッドのコンテキストによっては当然のごとく期待通りの値を返さない。

public static String getMainClassName() {
    StackTraceElement[] traces = Thread.currentThread().getStackTrace();
    return traces[traces.length-1].getClassName();
}

ということで、JVMUtilシリーズ第2弾はJVM自身が起動された際に指定されたエントリポイントとなったメインクラスの名前を取得する処理を考えてみた。(第1弾は起動プロセス中のJVM情報を列挙する)

JVM情報を取得する方法は第1弾で解っているので、JVMが自身のLVMID(Local VM ID、つまりはPID)を知ることができればよいことになる。

public static String getMainClassName() throws MonitorException, URISyntaxException {
    RuntimeMXBean mBean = ManagementFactory.getRuntimeMXBean();
    String[] vmidAndHost = mBean.getName().split("@");
    if ( vmidAndHost == null || vmidAndHost.length < 2) {
        throw new MonitorException("invalid RuntimeMXBean name : " 
                + mBean.getName());
    }
    MonitoredHost localhost = MonitoredHost.getMonitoredHost("localhost");
    MonitoredVm vm = localhost.getMonitoredVm (new VmIdentifier ("//" + vmidAndHost[0]));
    return MonitoredVmUtil.mainClass(vm, true);
}

例によって、このメソッドもVMにアクセスするためにSunのJDKに添付されているtools.jarの実装に依存していることに注意が必要だ。

このメソッドの肝というか前提になっているのは、現在のOS(Windows Vista)でRuntimeMXBean#getName()が"PID@ホスト名"を返すことだ。従って、SunのJDKを使ったとしても他のOSで同様に動作するかは解らない。また、使用しているのはsun.jvmstat.monitorパッケージのクラスなので、予告無しに実装が変わる懸念が付いて回ることになる。オウンリスクで使用すべきだろう。

個人的にはオウンリスクだろうがなんだろうが、JVMの情報に手軽にアクセスできる手段が出来たのは有難い。一度やったことがある方は解ると思うがJNDIを使ってたくさんのコードを書くのは非常にしんどいのだ。

他のOSでJava6をすぐに動かせる環境をお持ちの方は、もし試す機会があればPIDとメインクラス名が正しく取得できたかどうかを教えて頂けると有難い。
追記:
MonitoredVmUtil.mainClassには問題がありそうだ。JDKのドキュメントにはこうある。

Return the main class for the target Java application. Returns the main class or the name of the jar file if the application was started with the -jar option. 

"-jar"オプションで起動されたJVMから返すmailClassはjarファイルの名前であって実際のメインクラスでは無い、ということだ。う〜ん、微妙に使えないなこりゃ。