Java 1.7.0_21 および _25 を使用して、Mac OS X 10.8.4 で実行されている Java Web Start アプリケーションで ProcessBuilder を使用して特定のコマンドを実行しようとすると、問題が発生しました。
ProcessBuilder を使用して「/usr/sbin/system_profiler」コマンドを実行し、クライアント マシンに関する情報 (CPU 名、プロセッサ数など) を取得します。
次の方法で Web Start アプリケーションを起動すると、コマンドが適切に実行されます。
- URL引数のみのコマンドラインjavaws
- Web Start で作成されたアプリケーション バンドルと同じ引数を持つコマンド ライン javaws
- Java Web Start システム設定のキャッシュされたアプリケーション ダイアログ
次の方法で起動すると、コマンドが正しく実行されません。
- 初回起動時に Web Start によって作成される Mac アプリケーション バンドルへのショートカット
Web Start ショートカットを使用して起動した場合、"system_profiler" プロセスが子 "system_profiler" を作成し、それが独自の子を作成し、最終的に次のエラーが発生するまで続きます (おそらくプロセスの可用性が使い果たされたとき)。
Failed to gather report from '/usr/sbin/system_profiler -xml SPHardwareDataType -detailLevel full', exception: couldn't fork: errno 35
注: 上記のエラーは、「/usr/sbin/system_profiler」が「SPHardwareDataType」引数のみで起動された場合でも発生します。
この問題は、他のコマンドの (確かに小さい) ランダム サンプリングでは発生しないため、「system_profiler」コマンドに固有のようです。
コマンドを実行するために、いくつかの異なる方法も試しました。
/usr/sbin/system_profiler
/usr/sbin/system_profiler SPHardwareDataType
/bin/sh -c /usr/sbin/system_profiler
/bin/sh -c /usr/sbin/system_profiler SPHardwareDataType
/bin/tcsh -c /usr/sbin/system_profiler
すべての組み合わせで同じ動作が得られます。
アプリケーション バンドル Info.plist には、「WebStartArgs」キーの下に次のキーと値のペアが含まれていました。
- Item 0: -localfile
- Item 1: -online
- Item 2: -J-Djnlp.application.href=http://localhost:8080/test/test.jnlp
- Item 3: /Users/blah/Library/Application Support/Oracle/Java/Deployment/cache/6.0/40/744be8-7ce41b1a
上記のように、同じ引数を使用してコマンドラインから起動しようとしましたが、問題は発生しません。
以下は、問題を示す簡素化されたテスト アプリケーションです。
import java.io.*;
import java.util.Arrays;
public class TestProcessBuilder {
public static void main(String[] commands) throws IOException, InterruptedException {
System.out.println("Args: " + Arrays.toString(commands));
final ProcessBuilder pb = new ProcessBuilder(commands).redirectErrorStream(true);
System.out.println("Environment: " + pb.environment());
final Process p = pb.start();
final StringBuilder buffer = new StringBuilder();
Thread t = new Thread() {
@Override
public void run() {
InputStream stdoutStream = null;
try {
stdoutStream = new BufferedInputStream(p.getInputStream());
for(;;) {
int c = stdoutStream.read();
if(c == -1)
break;
buffer.append((char) c);
}
System.out.println("stdout buffer: " + buffer);
} catch(IOException e) {
throw new RuntimeException(e);
} finally {
close(stdoutStream);
}
}
};
t.start();
int result = p.waitFor();
t.join();
if(result != 0) {
throw new RuntimeException("Error result: " + result + " - " + buffer);
}
}
private static void close(Closeable closeable) {
try {
if(closeable != null)
closeable.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
注意として、プロセス出力を処理するための別のスレッドなしでも試しました
簡単なテスト アプリケーションの JNLP ファイルは次のとおりです。
<jnlp spec="1.0+" codebase="http://localhost:8080/test" href="test.jnlp">
<information>
<title>Test Process Builder</title>
<vendor>Test Vendor</vendor>
<homepage href="http://www.test.com" />
<description>Launches Test Process Builder</description>
<description kind="short">Test Process Builder</description>
<shortcut online="true">
<desktop />
</shortcut>
</information>
<security>
<all-permissions/>
</security>
<update check="always" policy="always"/>
<resources>
<java href="http://java.sun.com/products/autodl/j2se" version="1.7.0_21+"/>
<property name="jnlp.versionEnabled" value="false"/>
<jar href="lib/test.jar" download="eager" main="true"/>
</resources>
<application-desc main-class="netmedical.util.TestProcessBuilder">
<argument>/bin/sh</argument>
<argument>-c</argument>
<argument>/usr/sbin/system_profiler SPHardwareDataType</argument>
</application-desc>
</jnlp>
また、ローカル ファイル システムを参照するように JNLP を変換しようとしました。
codebase="file://localhost/Library/Tomcat/webapps/test"
となり、同じ現象が発生します。
唯一の違いは、Mac アプリケーション バンドルを起動するときのランタイム環境に関係があるはずですが、その違いが何であるかはわかりません。
何か問題がありますか:
- Java アプリケーションからプロセスを起動する方法は?
- 出力の処理方法は?
- Web Start 展開構成?
- 他の?