Wrapper ネイティブ Exec の概要

概要

WrapperManager.exec()」関数は、 Java の Java-Runtime.exec()Java-ProcessBuilder に代わる強力な選択肢です。 初期の Java 実装は標準の fork() に大きく依存していましたが、WrapperManager.exec() 関数 は それよりもメモリ効率を高めるように設計されており、複数の起動構成を提供し、子プロセスを起動するためのシステムレベルのメカニズムを細かく調整できます。

重要な機能の一つは、子プロセスを親プロセスからシームレスにバインドまたはデタッチできることです。 メインの Java プロセスが(正常終了または予期せぬ終了に関わらず)終了した場合、 Wrapper は実行中のバインドされたプロセスを自動的にクリーンアップします。

一貫性を高めるため、WrapperManager.exec() 関数は Java の Runtime.exec() 関数にできる限り近い形で実装されています。

実行

スタート

このセクションでは、コマンドをどのように実行するのかを説明します。 (さらに詳細は、このページを読み進めながら、ステップバイステップで説明していきます)

WrapperProcess p = WrapperManager.exec("ls -lisa");

上記は、[-l]、[-i]、[-s]や[-a]パラメーターで、[ls]コマンドを実行し、そのプロセスを表現する[WrapperProcess]オブジェクトをアサインします。

もし、バイナリ(あるいはスクリプト)ファイルの完全なフルパスが渡されていない場合、 システム PATH ディレクトリ同様に、そのカレント作業ディレクトリが確認され、 最初に見つかったものが実行されます。 現在の作業ディレクトリからの相対アドレス指定もサポートされています。

注意

exec]コマンドは、 単一の文字列(String)として、あるいは同様に、文字列群(StringArray)として、 どちらでのコマンドでも受け入れます。 文字列群(StringArray)の場合、各パラメーターは、単一の配列フィールド(a single array field)に配置されます。

プロセスの I/O

GUI アプリケーションでない限り、作成された子プロセスは、 OS のバックグランドで動作し、通常は見えません。 そのプロセスとコミニケーションするために、 [WrapperProcess]クラスは、 そのプロセスへの出力やエラーメッセージ、同様に、そのプロセスへの入力にアクセスするために、 3つのメソッドを提供します。

BufferedReader で、返されたストリームオブジェクト(StreamObject)をラップすることを推奨します。 リーダーを開けるには、下記のとおりに手順を進めてください。

BufferedReader br = new BufferedReader( new InputStreamReader(p.getWrapperProcessInputStream()));

これは、チャイルド(子)がその出力を書き出すインプットストリーム(InputStream)のリーダーを確立します。 エラーストリーム(ErrorStream)からデータを読む、あるいは、チャイルド(子)へデータを書き出すには、 この例に似たように処理を進めてください。

一旦、リーダー/ライターが確立されると、データを読み込む/書き込むことができるようになります。

String line;
while ((line = br.readLine()) != null)
{
    System.out.println(line);
}
br.close();

注意

この例を追うと、 その子プロセスが、自分側のストリームを閉じるまで、全てのデータが読み込まれます のでご注意ください。

子プロセスのコンフィギュレーション

コマンドを実行するとき、 WrapperManager は、子プロセスを設定する可能性を提供します。 これは、[WrapperProcessConfig]オブジェクトを、 [exec()]関数へ引き渡すことで行われます。 次のセクションでは、WrapperProcessConfiguration によって作成されるコンフィギュレーションについて説明します。

切り離されたプロセス

setDetached()」メソッドは、サブプロセスが Wrapper から「切り離されて」動作するのかを設定します。 もし、プロセスが「切り離された」とマークされた場合、Wrapper がシャットダウンする時、 そのプロセスを終了する必要はありません。 もし、「デタッチ(切り離し)」マークがない場合、Wrapper は子プロセスの経過を追い、 そのペアレント(親)プロセスを終了するべき時には、その子プロセスの終了を試みます。

注意

デフォルトで、プロセスはそのペアレント(親)プロセスから切り離された状態で開始されません。

起動方式

setStartType()」メソッドは、サブプロセスが OS によって、どのように開始されるのか、起動方式を指定します。

警告

このプロパティは Windows では無効です。

  • [FORK_EXEC] :

    UNIX/Linux 上で、子プロセスを生成する一般的な手法です。 しかしながら、一部の OS(特に Solaris)上では、 このコールは、チャイルド(子)用に、初めにペアレント(親)のメモリを複製することになります。 z/OS 上では、この起動方式が Wrapper 初代リリースの時点でサポートされていません。 HP-UX システム上では、Wrapper は[fork()]ではなく、 [vfork]を使うように自動的に切り替えます。

  • [VFORK_EXEC] :

    子プロセスが親プロセスとコードやデータを共有することができる場合に、 [vfork()]関数は、[fork()]とは異なります。 これは、 もし[vfork()]が誤用された場合、 親プロセスの整合性にリスクを伴い、クローン動作が著しく加速します。 一部のシステム上では、[vfork]は[fork]と同じです。 macOS では、Wrapper ver. 3.7.0以降、この起動方式はサポートされなくなり、DYNAMIC に解決されます。

  • [POSIX_SPAWN] :

    プロセスが生成され、何もメモリ複製 POSIX_SPAWN API を起こしません。 これは、Linux、Solaris (10+)、AIX、z/OS、MacOS と FreeBSD 8+のみに有効です。(バージョン 3.5.46から)

  • [DYNAMIC] :

    最適な起動方式は、Wrapper が実行されている OS に応じて自動的に選択されます。 Wrapper version 3.7.0 以降、「DYNAMIC」 はデフォルトで「POSIX_SPAWN」に解決されます。 ただし、[WrapperProcessConfig.isWorkingDirectorySupported()]が「FALSE」を 返すプラットフォームでカスタム作業ディレクトリが設定されている場合、子プロセスが要求されたディレクトリ内で安全に実行されるように、 自動的に「FORK_EXEC」にフォールバックします。

注意

デフォルト値は、起動方式[DYNAMICでプロセスが起動されます。

作業ディレクトリ

setWorkingDirectory()」は、サブプロセスの作業ディレクトリを指定するか、 サブプロセスがカレントプロセスの作業ディレクトリを継承する場合には、「NULL」を設定します。

Wrapper ver. 3.7.0以前は、POSIX_SPAWN を使用する際、作業ディレクトリを直接設定することはできませんでした。 その際の対処法として、実行したいコマンドをスクリプトでラップし、コマンドの実行前にディレクトリ変更を行うことが推奨されていました。

#!/bin/sh

chdir $1
shift
$*

Wrapper ver. 3.7.0 以降、setWorkingDirectory() メソッドは、Linux(glibc 2.29 以降)、macOS(10.15以降)、FreeBSD(13.1 以降)、Solaris(11.3 以降)など、ほとんどの最新の Unix プラットフォームで POSIX_SPAWN を使用する場合にサポートされます。 AIX はまだこの機能をサポートしていませんが、実装は前方互換性があり、IBM が将来の OS アップデートでこの機能を追加すれば自動的にサポートされます。 実行時に isWorkingDirectorySupported() メソッドを使用して、作業ディレクトリを設定する前にプラットフォームの互換性を確認することができます。

注意

もし、このプロパティが設定されない場合、サブプロセスは、その親プロセスから作業ディレクトリを継承します。

wrapper.child.allowCWDOnSpawn プロパティ(Wrapper ver. 3.7.0 以降、非推奨)

wrapper.child.allowCWDOnSpawn]プロパティでは、 [POSIX_SPAWN]や[DYNAMIC] 起動方式のとき、Wrapper が作業ディレクトリの変更を試みるかをコントロールします。

警告

このプロパティは元々実験的な機能として導入されましたが、マルチスレッドアプリケーションでは安全ではないため、 Wrapper ver. 3.7.0 で非推奨となりました。 これは、親 JVM のグローバル作業ディレクトリを一時的に変更するため、深刻な競合状態を引き起こす可能性があるためです。 これにより、同時実行スレッドが相対ファイルパスを正しく解決できなくなり、プロセスを生成しようとするネイティブ(JNI)コードが破損する可能性があります。

ほとんどの最新の Unix プラットフォームでは、このプロパティは必須ではなく、代替の 「setWorkingDirectory()」メソッドが推奨されます。(Wrapper ver. 3.7.0 以降を使用している場合) 作業ディレクトリの設定が現在のプラットフォームでサポートされているかどうかを確認し、実行時エラーを回避するには、 「isWorkingDirectorySupported()」メソッドを使用してください。

このプロパティのデフォルト値は「FALSE」に設定されます。

wrapper.child.allowCWDOnSpawn=TRUE

環境を設定する

setEnvironment()」メソッドは、作成されたサブプロセスの環境を指定します。

このフィールドは文字配列で、各エレメントは「名前=値」形式で、環境変数を設定します。

注意

もし、このプロパティが設定されない場合、 サブプロセスは、そのペアレント(親)プロセスから環境を継承します。

チャイルド終了にソフトタイムアウトを設定する

Wrapper バージョン 3.5.5 から、 各プロセスにそれぞれのソフトタイムアウトを指定することが可能になり、 Wrapper は子プロセスに自力で終了する機会が与えられますが、 [java.lang.Process.destroy()]メソッドは常に強制シャットダウンを引き起こし、プロセスに自力で終了する機会を与えません。 Wrapper バージョン 3.5.5 より以前では、機能性を一定に保つために、タイムアウトは常にデフォルトで5秒でした。

このタイムアウトは、子プロセスが[WrapperProcessConfig]クラスで作成されているときに指定することができます。

WrapperProcessConfig wpConfig = new WrapperProcessConfig().setSoftShutdownTimeout(10);

上記のコンフィギュレーション例では、そのコンフィギュレーションで作成されたとおり、 Wrapper を最大10秒間まで子プロセス待ちにさせます。 もし、子プロセスが10秒以内に終了しない場合、Wrapper はその子プロセスを強制終了させます。

タイムアウトの可能な値は、次のとおりです。

  • [>0] :

    強制終了される前に、子プロセスが自力で終了するまで、Wrapper が待機する秒数を指定します。

  • [0] :

    Wrapper が即座に子プロセスの終了を強制します。

  • [-1] :

    Wrapper は子プロセスを強制終了しませんが、 その子プロセスが自力で終了するまで永久に待機します。

アクティブユーザー用に子プロセスを作成する

Windows 上で、 [WrapperProcessConfig.setCreateForActiveUser(boolean)]は、サービスが動作しているアクティブセッション(OS の「SE_TCB_NAME」権限を持つユーザー)ではなく、現在のアクティブセッション内で、子プロセスが起動するかどうかを指定します。 Windows 以外のプラットフォーム上で、あるいはコンソールモードで起動されている場合、この設定は静かに無視されます。

デフォルト値は「FALSE」に設定されています。