高度な検索
Developer Connection
Member Login ログイン | ご入会 ADC連絡先

Technote 1168

The Care And Feeding of Runtime.exec


目次

Runtime.exec のプラットフォーム依存

アプリケーションへのパス

引数の設定

Java の起動

Process オブジェクトの処理

サンプル: ブラウザでの URL のオープン

R untime.exec( ) はおそらく、Java API セットの中でもクロスプラットフォームでの互換性が乏しい部分の 1 つです。Runtime.exec( ) では、OS へのコマンドラインインタフェースの存在と、任意のパラメータを受け取ることのできる任意のアプリケーションを起動する機能を前提とします。とはいえ、Runtime.exec( ) を使用しなければならない機会はかなりあります。たとえば、Web ブラウザで URL をオープンしたり、新しい Java プロセスを発生させる場合には Runtime.exec( ) が必要になります。このテクニカルノートでは、MRJ 2.1 の Runtime.exec のインプリメンテーションと、それが JDK のインプリメンテーションとどのような点が異なるかについて説明します。


Runtime.execのプラットフォーム依存

標準 Java のメソッドであるRuntime.exec は、それが実行されているマシン上で新しいプロセスを起動します。Runtime.exec には、スペースで区切った引数の文字列または引数文字列の配列を引数とするコマンドラインを渡します。『The Java Class Libraries』には、次のような説明があります。

「このメソッドは、引数で指定したプラットフォームに依存するプログラムを実行します。実行するプログラムは、プラットフォームに依存する絶対パス名を使って指定するか、プラットフォームに依存する検索パスを使って検出されるプラットフォームに依存するコマンド名として指定できます。」

「exec は Process オブジェクトを返し、これは、新しく作成されたプロセスの標準入力、標準出力、および標準エラー (ストリーム) を取得するためのメソッドを持ちます。」(太字による強調はこのテクニカルノートの著者が追加しました)

この説明の中では、このメソッドの動作の大部分がプラットフォームに依存するということを微妙な表現で暗に示そうとしています (しかも、ここで言及されている「標準」ストリームでさえ、Mac OS ではまったく標準ではありません)。

したがって、このメソッドを使用する場合、最終的に完成したコードが一部のオペレーティングシステムではまったく実行できないか、あるいは正しく実行できないということは容易に想像できます。Unix や Windows とは異なり、Mac OS は、コマンドラインベースの OS の上に薄い GUI の化粧板を貼り付けたものではないため、このメソッドの使用には不都合があります。Mac OS そのものにはコマンドラインのようなものは存在しないため、MRJ が想像力豊かなデベロッパによる Runtime.exec のさまざまな使用方法をサポートすることはかなり困難です。

とはいうものの、Apple ではさまざまな試みを行い、それ以前のバージョンと比較すると、MRJ 2.1 がより多くの Runtime.exec の典型的な使い方をサポートしているということが明らかになりました。このテクニカルノートでは、MRJ 2.1 によってサポートされていることと、サポートされていないことを詳細に説明します。

JConfig

サードパーティのコード (ネイティブライブラリを含む) を組み込み予定がある場合は、Samizdat Productions のフリーウェアである JConfig ライブラリの採用を検討することをおすすめします。

JConfig は、「コア Java API を強化するクロスプラットフォームライブラリです。JConfig を使用すると、標準 Java クラスライブラリによって提供されている方法よりもはるかに先進的な方法で、ファイル、Web ブラウザ、プロセス、ファイルタイプ、およびその他のシステムレベルの項目を対象とする作業を行うことができます。」(マニュアルより)

JConfig はRuntime.exec よりもはるかに強力であり、JConfig を使用することで、ユーザが通常使用している Web ブラウザ、あるいは任意の URL タイプに対応したユーザの好むヘルパーアプリケーションの検索など、扱いにくく、より高レベルな問題を取り扱うことができるようになります。 JConfig は、Mac OS、Windows、および Unix 上で実行でき、しかも MRJ を使って開発を行う多数のデベロッパがJConfig を推奨しています (もちろん、Apple には JConfig または Samizdat Productions との公式な提携関係があるわけではなく、このライブラリの信頼性を保証することもできません。しかし、Apple ではこの製品が「クール」であると考えています)。


Back to top

アプリケーションへのパス

Mac OS には検索パスはありません。このため、実際のファイルパスを使ってアプリケーションを指定する必要があります。たとえば、「netscape」というアプリケーションを直接参照することは不可能です。しかし、この処理を実行するためにはいくつかの方法が用意されています。

相対パス

起動しようとしているアプリケーションが実行中のアプリケーションを基準とする既知の位置にある場合は (つまり、両方のアプリケーションが同一のインストールファイルの一部である場合)、現在のアプリケーションを基準とするパスを指定することができます。この方法は、JBindery を使ってビルドしたアプリケーションを実行していて、目的のコードを JBindery そのものから直接実行しない場合だけ、信頼できる動作を行います。

ユーザ初期設定

起動するアプリケーションが独自のインストールファイルの一部でない場合は (たとえば、それが SimpleText や Web ブラウザのような標準的なアプリケーションである場合)、絶対パスを指定する必要があります。

絶対パスを指定する上で、最も合理的かつクロスプラットフォームに対応した方法といえば、パスを初期設定ファイルに格納することです。初期設定がまだ存在しない場合、あるいは初期設定ファイルに格納されているパスを使用しようとしてうまくいかない場合は、ダイアログボックスを表示してユーザにアプリケーションの選択を要求し、そのパスを初期設定ファイルに格納してください。初期設定ファイルを使って正しいパスを設定する方法については、「Technote 1134 The Preferences Problem」を参照してください。

クリエータによる検索

Mac 固有のコードを使用するつもりがある場合は、Mac OS 独自の仕組みを使用し、アプリケーションのクリエータコードを指定してアプリケーションを自動的に検索するのが望ましい方法といえます。クリエータコードとは、そのアプリケーションに割り当てられている 4 文字の一意なコードのことです。

任意のアプリケーションのクリエータを知るには、ResEdit の「Get File/Folder Info」コマンドを使用します。次に、よく使用されるいくつかのアプリケーションのクリエータを示します。

Microsoft Internet Explorer

MWIE

Netscape Navigator

MOSS

SimpleText

ttxt

Finder

FNDR

MRJ SDK (大部なマニュアルを含む) の一部として提供されている MRJToolkit ライブラリには、クリエータコードを引数として取り、アプリケーションを指す File オブジェクトを返す findApplication メソッドが用意されています。このメソッドは次のように使用します。

import com.apple.mrj.*;               
...
                  
File app = MRJFileUtils.findApplication(new MRJOSType("ttxt"));

JConfig による自動的な検索

Samizdat Productions から提供されている JConfig ライブラリには、任意の URL のタイプに対応したユーザが好むヘルパーアプリケーションを検索する機能が用意されています。この処理は、Runtime.exec の最も一般的な使い方の 1 つです。


Back to top

引数の設定

Mac OS では、AppleEvent と呼ばれるメカニズムを使って、プロセスから別のプロセスにメッセージを送信します。AppleEvent は、コマンドライン引数よりもはるかに高レベルであり、しかも豊富なデータ型とタグのセットを含んでいます。しかし、このことは、MRJ が任意の引数文字列を受け付けることができず、引数文字列を意味のある方法でアプリケーションに送信することができないということを意味します (最初のセクションの「プラットフォーム依存」に関する説明を思い出してください)。

MRJ では、よく使用される特定のコマンドライン引数のタイプを検出し、それらを AppleEvent に変換しようとします。一般に、MRJ が認識できる引数のタイプは、ファイルパスと URL の 2 つです。コロン (:) を含む引数は URL であると解釈され、その他の引数はファイルパスであると解釈されます (とはいえ、これらの規則は、別の Java プロセスを起動するときには適用されません。詳細については、次のセクションを参照してください)。

注意:
バージョンが 2.1 より前の MRJ では、URL パラメータはサポートされておらず、すべての引数が単純にファイルパスと見なされます。

(AppleEvent savvy であるためには、ファイルパスは単一の 'odoc' イベントとして送信され、URL は個別の'GURL' イベントとして送信されることを知っておく必要があります)

いくつかの注意すべき問題があります。

複数回にわたる起動を行わない

Mac OS では、同一のアプリケーションを複数回にわたって起動することは許可されません。このため、実行中のアプリケーションで Runtime.exec を呼び出す場合、または同一のアプリケーションで複数回にわたって Runtime.exec を呼び出す場合は、そのアプリケーションの同一のインスタンスがそれぞれのリクエストを受け取り、それぞれの項目をオープンすることになります。

通常、URL は新しいウィンドウをオープンしない

少なくとも Web ブラウザの慣例として、新しいウィンドウをオープンするのではなく、前のコンテンツが置き換えられて、アクティブウィンドウに複数の URL が表示されます (ただし、ファイルは新しいウィンドウにオープンされます)。これは、ブラウザに URL 引数を送信すると、その前に送信した引数は事実上無視されるということを意味します (たとえば、ブラウザに 2 つの URL を渡すと、ブラウザではまず第 1 の URL を表示しようとし、すぐに同じウィンドウに第 2 の URL を表示しようとします。また、1 つのファイルと 1 つの URL を渡すと、ファイルを表示するために新しいウィンドウがオープンしますが、その後、URL が同じウィンドウに表示されます)。


Back to top

Java の起動

Runtime.exec を使って実行できる最も役に立つ処理の 1 つが新しい Java プロセスの起動であることは明らかです。このことは、起動しようとしているコードに、現在のプロセスからの完全な独立性を与える必要がある場合に役立ちます。たとえば、javac などの一部の Java ツールは、メモリを再利用したり、ファイルを閉じたりすることはなく、実際的な目的のために、それが終了するときに OS によってクリーンアップされる独立したプロセスとして実行される必要があります。

MRJ 2.1 では、このことをさらに利用するため、いくつかの特殊なケース (「ハック」とも呼ばれます) が追加されました。先頭の引数で指定したアプリケーション/コマンドが既存のファイルを指さず、その引数の中に「Java」という下位文字列 (大小文字の区別なし) が含まれている場合は、あたかもコマンドラインから JDK を呼び出しているかのように、Java プロセスを起動しようとしているものと見なされます。

注意:
バージョンが 2.1 より前の MRJ では、この機能はサポートされていません。

たとえば、次のように Java コンパイラを呼び出すことができます。

String args = {"java", "sun.tools.javac.Main", ...javac argument list...};
Runtime.getRuntime().exec(args);

逆にいえば、対象が Java プロセスであることがわかっているため、次のようなより一般的な機能も使用できるようになります。

  •  
  • 自由に任意の引数を渡すことができます。引数が URL またはファイルである必要はありません。これらは、アプリケーションのメインクラスの main() メソッドに文字列として直接渡されます。
  • Process オブジェクト (詳細は後述します) を使って、Java プロセスの標準入力/出力/エラーストリームにアクセスすることができます。

     

'java' に適用できる特殊なフラグ

JDK の'java' コマンドには、いくつかのフラグパラメータを指定できます。 MRJ 2.1 では、次のサブセットがサポートされています。

フラグ

目的

ステータス

-help

ヘルプメッセージを表示して終了

サポート済み

-version

バージョンを表示して終了

エラー

-v

冗長モードをオンに

エラー

-classpath

クラス検索パスを設定*

サポート済み

-D

システムプロパティを設定

サポート済み

-X

非標準オプションに関するヘルプを表示して終了

エラー

-Xdebug

リモートデバッガを有効に

エラー

-Xnoasyncgc

非同期ガーベージコレクションを無効に

サポート済み

-Xnoclassgc

クラス GC を無効に

サポート済み

-Xss

最大のネイティブスタックサイズを設定

無視

-Xoss

最大の Java スタックサイズを設定

無視

-Xms

Java ヒープの初期サイズを設定

無視

-Xmx

最大の Java ヒープサイズを設定

無視

-Xprof

メソッドプロファイリングを有効に

無視

-Xiprof

インストラクションプロファイリングを有効に

無視

-Xhprof

ヒーププロファイリングを有効に

無視

-Xverify

バイトコードの検証を有効に

無視

MRJ の将来のバージョンでは、これらのフラグをより多くサポートされる可能性があります。

* -classpath に関する注意: JDK 1.2 と同様、正規のシステムクラスパスに「追加」を指定するだけで十分です。classes.zip、JDKClasses.zip、または rt.jar を追加する必要はありません。なお、これらを追加すると、エラーが返されることになります。クラスパスのエントリは、絶対パスまたは現在の作業ディレクトリに対する相対パスのどちらでもかまいません。デフォルトの設定で、現在のアプリケーションを含むディレクトリが作業ディレクトリになります。


Back to top

Process オブジェクトの処理

Runtime.exec メソッドが正常に実行されると、Process オブジェクトが返されます。このオブジェクトを使用すると、プロセスのステータスのチェック、プロセスの強制的な終了、およびプロセスの入力/出力/エラーストリームへのアクセスを行うことができます。

これらの大部分はアドバタイズされたものとして動作しますが、後で使用することには問題があります。つまり、Mac OS には、アプリケーションに添付されたテキストストリームという概念がないため、それらにアクセスすることは意味をなしません。Process に通常のアプリケーションに関連づけられたストリームを要求しても、MRJ は null を返すだけです。

ただし、前述したように、Java プロセスだけは例外です。これらは Java コードだけを実行する特殊なプロセスであるため、コンソール I/O ストリームを持ち、Process API を使って、それらにアクセスすることができます。


Back to top

サンプル: ブラウザでの URL のオープン

次に、Levi Brown によるサンプルコードを示します。このサンプルコードは、Web ブラウザで URL をオープンするためのクロスプラットフォーム savvy な方法を具体的に示しています。このサンプルコードを実行すると、ユーザが通常使用している Web ブラウザを検索するため、ユーザに入力を要求するファイルダイアログが表示されます。

import java.awt.Frame;
                  import java.awt.FileDialog;
                  import java.io.File;
                  import java.io.IOException;
                  
                  public class ExecTest extends Frame
{
                  
    public static void main(String[] args)
    {
        new ExecTest();
        System.exit(0);
    }
                  
    public ExecTest()
    {
        String browserName;
        String url = "http://developer.apple.com/java/";
                  
        //使用するブラウザをユーザに検索させるファイルダイアログをセットアップする
        FileDialog fileDialog = new java.awt.FileDialog(this);
        fileDialog.setMode(FileDialog.LOAD);
        fileDialog.setTitle("Choose the Web browser to use:");
        fileDialog.setVisible(true);
                  
        //ダイアログからパス情報を取得して、それを検証する
        String resultPath = fileDialog.getDirectory();
        String resultFile = fileDialog.getFile();
        fileDialog.dispose();
        if(resultPath != null && resultPath.length() != 0
                   && resultFile != null && resultFile.length() != 0)
        {
            File file = new File(resultPath + resultFile);
            if(file != null)
            {
                browserName = file.getPath();
                  
                try{
                    //ブラウザを起動し、目的の URL を渡す
                    Runtime.getRuntime().exec(new String[] {browserName, url});
                }
                catch (IOException exc)
                {
                    exc.printStackTrace();
                }
            }
        }
    }
}

実際に使用する場合は、このサンプルを修正して、ブラウザ (browserName) の位置を初期設定ファイルに格納し、キャッシュに格納されたブラウザが見つからない場合だけユーザにブラウザの指定を要求するようにしてください。プロンプトの文字列をローカライズすることもできます。

また、前述したように、アプリケーションシグネチャによってブラウザを検索したり、JConfig を使って、URL をオープンするプロセス全体を処理することもできます。


Further References


Back to top


更新日: 1999 年 5 月 17 日