Go to Contents Go to Java Page
J2SE 1.5 虎の穴
 
 

外部アプリ起動がプチ簡単に ProcessBuilder

 
 
Tiger Runtime#exec
 
 

外部アプリの起動は Runtime#exec メソッドを使用すれば可能です。でも、なぜか外部アプリ起動用のクラスが Tiger では作られています。その名も ProcessBuilder。しかし、機能は全然変わりません。

なにが変わったかというと、使い方が簡単になったということなのです。

 

 
 
Tiger 使ってみよう
 
 

変わらないといっていてもしょうがないので、使ってみましょう。

サンプルのソース ProcessBuilderTest.java

このサンプルは java を -version オプションで起動するものです。

import java.io.InputStream;
import java.io.IOException;
 
public class ProcessBuilderTest {
    public static void main(String[] args) {
        try {
            ProcessBuilder builder = new ProcessBuilder("java", "-version");
            Process process = builder.start();
 
            InputStream stream = process.getErrorStream();
            while (true) {
                int c = stream.read();
                if (c == -1) {
                    stream.close();
                    break;
                }
                System.out.print((char)c);
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

ProcessBuilder クラスのコンストラクタの引数は、起動するプログラムとその引数です。コンストラクタの引数は可変長引数になっているので、プログラムの引数をいくつでも書くことができます。

プログラムの起動は start メソッドを使用します。start メソッドの戻り値は Process オブジェクトなので、後は Runtime#exec メソッドの時と同じになります。

ここで、Process オブジェクトから標準エラー出力を取っているのはバージョン情報がエラー出力に書きだされるからです。

実行すると、

C:\examples>java ProcessBuilderTest
java version "1.5.0-beta2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta2-b51)
Java HotSpot(TM) Client VM (build 1.5.0-beta2-b51, mixed mode, sharing)
   
C:\examples>

となり、ちゃんと起動されていることが分かると思います。

せっかくなので環境変数をセットしてみましょう。

サンプルのソース ProcessBuilderTest2.java
Sample.java

ProcessBuiderTest2 クラスは Sample クラスを起動します。Sample クラスは環境変数 "SAMPLE" を出力しているだけです。

public class Sample {
    public static void main(String[] args) {
        System.out.println("SAMPLE = " + System.getenv("SAMPLE"));
    }
}

ProcessBuilderTest2 は次のようになっています。

import java.io.InputStream;
import java.io.IOException;
import java.util.Map;
 
public class ProcessBuilderTest2 {
    public static void main(String[] args) {
        try {
            ProcessBuilder builder = new ProcessBuilder("java", "Sample");
            Map<String, String> env = builder.environment();
            env.put("SAMPLE", "sample");
 
            Process process = builder.start();
 
            InputStream stream = process.getInputStream();
            while (true) {
                int c = stream.read();
                if (c == -1) {
                    stream.close();
                    break;
                }
                System.out.print((char)c);
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

環境変数をセットするには、まず ProcessBuilder#environment メソッドで現在の環境変数の一覧を取得します。これは Map オブジェクトになっており、そのオブジェクトに追加、削除、更新を行うようにします。

単に Sample クラスだけを実行すると null と出力されますが、ProcessBuilderTest2 から起動すると sample と出力されます。

C:\examples>java Sample
SAMPLE is null

C:\examples>java ProcessBuilderTest2
SAMPEL is sample
   
C:\examples>

これ以外にも、directory メソッドでワーキングディレクトリを指定したり、command メソッドで起動するプログラムを変更することもできます。

 

 
 
Tiger Runtime#exec はどうなっているのか
 
 

ProcessBuilder クラスができた今、Runtime#exec メソッドはどうなっているのでしょうか。Runtime#exec メソッドは引数によって 6 種類ありますが、一番細かい制御を行うことのできる Runtime#exec(String[] cmdarray, String[] envp, File dir) の定義を見てみましょう。

    public Process exec(String[] cmdarray, String[] envp, File dir)
                                                    throws IOException {
        return new ProcessBuilder(cmdarray)
            .environment(envp)
            .directory(dir)
            .start();
    }

なんのことはない ProcessBuilder クラスを使用しているようになっていました。

 

 
 
Tiger おわりに
 
 

ようは ProcessBuilder クラスはリファクタリングして、扱いやすくしただけのようです。それでも、Runtime クラスがごちゃごちゃしているので、いいのかもしれません。

それよりも重要なのは、今まで Runtime#exec はいろいろとバグを含んでいたということです。これは Bug Parade の 4109888 Semantics of external process is not defined in JLS です。

もし、ProcessBuilder クラスとして書き直すことで、このバグが直っているのであれば、万々歳なのですが... beta 2 ではまだ Bugs Fixed in J2SE 1.5.0 Tiger にはのっていないようです。ちょっと、残念。

 

今回使用したサンプルはここからダウンロードできます。

 

(Jun. 2004)

 
 
Go to Contents Go to Java Page