通常は Java(TM) によって 処理される機能でも、ILE RPG を Java とともに 使用する場合には、ユーザーの RPG コードで処理される必要があるものがいくつかあります。RPG コンパイラー がこれらのいくつかについてはユーザーに代わって処理しますが、一部についてはユーザー自身が処理しなければならないものもあります。 この節では、この処理を行なういくつかのサンプル RPG ラッパーを示し、これらを呼び出す方法とタイミングを説明し、JNI 例外を処理する方法を提案します。
これらの JNI ラッパー関数を保持するために作成するモジュールは以下のステートメントで始める必要があります。
H thread(*serialize) H nomain /define OS400_JVM_12 /copy qsysinc/qrpglesrc,jni
JNI 関数用の以下の RPG ラッパーを説明します。詳しい作業例については、以下の図 91 を参照してください。
RPG コードの、Java を使用するセクションの前で JNI 関数 PushLocalFrame を呼び出し、 RPG コードの最後のセクションで PopLocalFrame を呼び出すと、複数のローカル参照を同時に解放できます。 PopLocalFrame を呼び出すと、PushLocalFrame の呼び出し以降に作成されたローカル参照すべてが解放されます。 これらの JNI 関数のパラメーターについて詳しくは、http://java.sun.com にある JNI の資料を参照してください。
D JNI_GROUP_ADDED... D c 0 D JNI_GROUP_NOT_ADDED... D c -1 D JNI_GROUP_ENDED... D c 0 *---------------------------------------------------------------- * beginObjGroup - 後にまとめて削除できる新しいオブジェクトの * グループの開始 *---------------------------------------------------------------- P beginObjGroup b export D beginObjGroup pi 10i 0 D env * const D capacityParm 10i 0 value options(*nopass) D rc s 10i 0 D capacity s 10i 0 inz(100) /free JNIENV_p = env; if (%parms >= 2); capacity = capacityParm; endif; rc = PushLocalFrame (JNIENV_p : capacity); if (rc <> 0); return JNI_GROUP_NOT_ADDED; endif; return JNI_GROUP_ADDED; /end-free P beginObjGroup e *---------------------------------------------------------------- * endObjGroup - 最後に開始されたオブジェクトのグループの * 終了 *---------------------------------------------------------------- P endObjGroup b export D endObjGroup pi 10i 0 D env * const D refObject P o class(*java:'java.lang.Object') D const D options(*nopass) D newObject P o class(*java:'java.lang.Object') D options(*nopass) D retVal s o class(*java:'java.lang.Object') D refObject s like(refObjectP) inz(*null) D newObject s like(newObjectP) /free JNIENV_p = env; if %parms() >= 2; refObject = refObjectP; endif; newObject = PopLocalFrame (JNIENV_p : refObject); if %parms() >= 3; newObjectP = newObject; endif; return JNI_GROUP_ENDED; /end-free P endObjGroup e
Java コンストラクターを使用してオブジェクトを作成した場合、またはユーザーにオブジェクトを戻す Java メソッドを呼び出した場合、このオブジェクトは、Java のガーベッジ・コレクションがもうユーザーがそのオブジェクトを必要としていないと知ったときに破棄されるまでの間だけ、使用可能であることになります。 これは、ネイティブ・メソッド (java によって呼ばれる) が戻るときにネイティブ・メソッドのために発生しますが、そうでない場合は、オブジェクトを必要としなくなった Java に明示的に通知しない限り、決して発生しません。 これは、RPG ラッパー・プロシージャー freeLocalRef を呼び出すことによって行ないます。
CALLP freeLocalRef (JNIEnv_P : string);
図 87 に、freeLocalRef のサンプル・ソース・コードを示します。
/*------------------------------------------------------*/
/* freeLocalRef */
/*------------------------------------------------------*/
P freeLocalRef...
P B EXPORT
D freeLocalRef...
D PI
D env * VALUE
D localRef O CLASS(*JAVA
D : 'java.lang.Object')
D VALUE
/free
jniEnv_P = env;
DeleteLocalRef (env : localRef);
/end-free
P freeLocalRef...
P E
ユーザーにパラメーターとして渡されたか、または Java メソッドあるいはコンストラクターを呼び出すことによって作成されたか、いずれかの Java オブジェクトに対して、ユーザーが参照を行なう場合で、かつそのオブジェクトをユーザーのネイティブ・メソッドが戻った後も使用したい場合は、Java に対してそのオブジェクトを永続または「グローバル」にしたいということを伝える必要があります。これは、RPG ラッパー・プロシージャー getNewGlobalRef を呼び出し、結果をグローバル変数に保管することによって行ないます。
EVAL globalString = getNewGlobalRef (JNIENV_P : string);
図 88 に、getNewGlobalRef のサンプル・ソース・コードを示します。
/*------------------------------------------------------*/ /* getNewGlobalRef */ /*------------------------------------------------------*/ P getNewGlobalRef... P B EXPORT D getNewGlobalRef... D PI O CLASS(*JAVA D : 'java.lang.Object') D env * VALUE D localRef O CLASS(*JAVA D : 'java.lang.Object') D VALUE /free jniEnv_P = env; return NewGlobalRef (env : localRef); /end-free P getNewGlobalRef... P E
グローバル参照を作成した場合で、そのオブジェクトが既に必要ではなくなったことが わかっている場合は、ユーザー側に関しては、このオブジェクトを次回 Java が ガーベッジ・コレクションを行なう時点で破棄してよい、ということを Java に 対して伝える必要があります。 (オブジェクトは、グローバル参照の対象でなくなり、かつ Java それ自身の中で他から参照されなくなった場合にのみ、破棄されます。) Java に、ユーザーがもうオブジェクトへの参照を必要としなくなったことを伝えるためには、RPG ラッパー・プロシージャー freeGlobalRef を呼び出します。
CALLP freeGlobalRef (JNIEnv_P : globalString);
図 89 に、freeGlobalRef のサンプル・ソース・コードを示します。
/*------------------------------------------------------*/
/* freeGlobalRef */
/*------------------------------------------------------*/
P freeGlobalRef...
P B EXPORT
D freeGlobalRef...
D PI
D env * VALUE
D globalRef O CLASS(*JAVA
D : 'java.lang.Object')
D VALUE
/free
jniEnv_P = env;
DeleteGlobalRef (env : globalRef);
/end-free
P freeGlobalRef...
P E
ユーザーの RPG コードが Java メソッドを呼び出す準備ができた時点でまだ JVM が作成されていない場合は、RPG は、デフォルトのクラスパスに加えてユーザーの CLASSPATH 環境変数の中のクラスパスを使用して、ユーザーの代わりに JVM を作成します。ただし、ユーザー自身が JVM を作成したい場合は、図 90 の最後の部分にあるこのコーディングの例を参照することができます。
何らかの JNI 機能を呼び出す必要がある場合は、QSYSINC/QRPGLESRC からの /COPY ファイル JNI を使用します。 JNI 機能のほとんどは、プロシージャー・ポインターを介して呼び出されます。プロシージャー・ポインターは、「JNI 環境ポインター」と呼ばれるポインターにそれ自身が基づいているデータ構造の一部です。このポインターは JNI /COPY ファイルの中で JNIEnv_P と呼ばれています。 このポインターを入手するには、JNI ラッパー・プロシージャー getJniEnv を呼び出します。
EVAL JNIEnv_P = getJniEnv();
図 90 に、getJniEnv のサンプル・ソース・コードを示します。
*---------------------------------------------------------------- * getJniEnv - JVM への接続または JVM の開始 * * パラメーター: * 1. inputOpts - 文字列の最後の文字で分離された * オプションの文字列。 * 例 * -Djava.pool.size=800;-Dcompile=none; * - JVM が既に開始されている場合は無視されます * - クラスパスは CLASSPATH 環境変数から取得されますが、 * -Djava.class.path オプションを使用して * このプロシージャーに渡すこともできます。 * 呼び出しの例: * env = getJniEnv() // デフォルトを使用 * env = getJniEnv('-Djava.poolsize=800;' * + '-Dcompile=none;') // 2 つのオプションを指定 * env = getJniEnv('-Djava.poolsize=800!' * + '-Dcompile=none!') // 分離文字として ! を使用 * *---------------------------------------------------------------- P getJniEnv b export D getJniEnv pi * D inputOpts 65535a varying const options(*nopass) D env s * inz(*null) /free env = attachJvm(); if (env = *null); if %parms() = 0 or %len(inputOpts) = 0; env = startJvm(); else; env = startJvm(inputOpts); endif; endif; return env; /end-free P getJniEnv e
*---------------------------------------------------------------- * startJvm - JVM の開始を試行 * * パラメーター: * 1. inputOpts - オプションの文字列は文字列の最初の文字で * 分離されます * - JVM が開始済みの場合は無視されます *---------------------------------------------------------------- P startJvm b export D startJvm pi * D inputOptsP 65535a varying const options(*nopass) D initArgs ds likeds(JavaVMInitArgs) D options ds likeds(JavaVMOption) occurs(10) D jvm s like(JavaVM_p) D env s like(JNIENV_p) inz(*null) D rc s 10i 0 D len s 10i 0 D prefix s 100a varying D pOptions s * inz(%addr(options)) D i s 10i 0 D classpath S 65535a varying * For handling the input options D splitChar s 1a D pos s 10i 0 D nextPos s 10i 0 D inputOpts s 65535a varying D inputOptsPtr s * D freeThisOccur s n dim(%elem(options)) inz(*off) /free monitor; initArgs = *allx'00'; JNI_GetDefaultJavaVMInitArgs (%addr(initArgs)); initArgs.version = JNI_VERSION_1_2; // 必要な場合は classpath オプションを追加 classpath = getClasspath(); if (%len(classpath) > 0); initArgs.nOptions = initArgs.nOptions + 1; %occur(options) = initArgs.nOptions; freeThisOccur(initArgs.nOptions) = *on; options = *allx'00'; prefix = '-Djava.class.path=:'; len = %len(prefix) + %len(classpath) + 1; options.optionString = %alloc (len); %str(options.optionString : len) = cvtToAscii(prefix) + cvtToAscii(classpath); endif;
// そのほかの受け渡されたオプションを追加 if %parms > 0 and %len(inputOptsP) > 0; inputOpts = cvtToAscii(inputOptsP); inputOptsPtr = %addr(inputOpts) + 2; splitChar = %subst(inputOpts : %len(inputOpts) : 1); pos = 1; dow pos <= %len(inputOpts); nextPos = %scan(splitChar : inputOpts : pos); len = nextPos - pos; %subst(inputOpts : nextPos : 1) = x'00'; initArgs.nOptions = initArgs.nOptions + 1; %occur(options) = initArgs.nOptions; options = *allx'00'; options.optionString = inputOptsPtr + pos - 1; pos = nextPos + 1; enddo; endif; if initArgs.nOptions > 0; initArgs.options = pOptions; endif; rc = JNI_CreateJavaVM (jvm : env : %addr(initArgs)); if (rc <> 0); env = *null; endif; rc = JNI_CreateJavaVM (jvm : env : %addr(initArgs)); if (rc <> 0); env = *null; endif; on-error; env = *null; endmon; // このオプション用に割り振られたストレージすべてを解放 for i = 1 to initArgs.nOptions; if (freeThisOccur(i)); %occur(options) = i; dealloc(n) options.optionString; endif; endfor; return env; /end-free P startJvm e
*---------------------------------------------------------------- * attachJvm - JVM への接続を試行 *---------------------------------------------------------------- P attachJvm b export D attachJvm pi * D attachArgs ds likeds(JavaVMAttachArgs) D jvm s like(JavaVM_p) dim(1) D nVms s like(jsize) D env s * inz(*null) D rc s 10i 0 /free monitor; rc = JNI_GetCreatedJavaVMs(jvm : 1 : nVms); if (rc <> 0); return *null; endif; if (nVms = 0); return *null; endif; JavaVM_P = jvm(1); attachArgs = *allx'00'; attachArgs.version = JNI_VERSION_1_2; rc = AttachCurrentThread (jvm(1) : env : %addr(attachArgs)); if (rc <> 0); env = *null; endif; on-error; env = *null; endmon; return env; /end-free P attachJvm e
*---------------------------------------------------------------- * getClasspath - CLASSPATH 環境変数を取得 *---------------------------------------------------------------- P getClasspath B export D getClasspath PI 65535A varying D Qp0zGetEnvNoInit... D PR * extproc('Qp0zGetEnvNoInit') D name * value options(*string) D envVarP S * /free envvarP = Qp0zGetEnvNoInit('CLASSPATH'); if (envvarP = *NULL); return ''; else; return %str(envvarP : 65535); endif; /end-free P getClasspath E
*---------------------------------------------------------------- * cvtToAscii - EBCDIC から ASCII への変換 *---------------------------------------------------------------- P cvtToAscii B export D cvtToAscii PI 65535A varying D input 65535A const varying D QDCXLATE PR extpgm('QDCXLATE') D len 5P 0 const D cnvData 65535A options(*varsize) D cnvTbl 10A const D cnvLib 10A const D retval S like(input) D retvalRef S 1A based(retvalPtr) /free retval = input; retvalPtr = %addr(retval) + 2; // set ptr after the length part QDCXLATE(%len(retval): retvalRef : 'QASCII': 'QSYS'); return retval; /end-free P cvtToAscii E
Java class class TestClass{ String name = "name not set"; TestClass (byte name[]) { this.name = new String(name); } void setName (byte name[]) { this.name = new String(name); } String getName () { return this.name; } } RPG program H THREAD(*SERIALIZE) H BNDDIR('JAVAUTIL') // (JAVAUTIL is assumed to the binding directory that lists // the service program containing the procedures described // below) /copy JAVAUTIL // (JAVAUTIL is assumed to be the source member containing the // prototypes for the procedures described below) D TestClass C 'TestClass' D StringClass C 'java.lang.String' D newTest PR O EXTPROC(*JAVA : TestClass D : *CONSTRUCTOR) D name 25A VARYING CONST D getName PR O CLASS(*JAVA : StringClass) D extproc(*JAVA : TestClass D : 'getName') D setName PR extproc(*JAVA : TestClass D : 'setName') D newName 25A VARYING CONST D newString PR O EXTPROC(*JAVA : StringClass D : *CONSTRUCTOR) D value 65535A VARYING CONST D nameValue PR 25A VARYING D extproc(*JAVA : StringClass D : 'getBytes') D myTestObj S LIKE(newTest) D myString S LIKE(newString) D env S LIKE(getJniEnv) /free // Let the RPG runtime start the JVM, by calling a // Java method before calling any JNI functions myString = newString (''); // Get the JNI environment pointer so that JNI functions // can be called. The "myString" object should be freed now
env = getJniEnv(); freeLocalRef (env : myString); // Set the beginning marker for an "object group" // so that any objects created between now and the // "end object group" can be freed all at once. beginObjGroup (env); // Create a Test object to work with // We do not want this object to be freed with the // other objects in the object group, so we make it // a permanent object myTestObj = newTest ('RPG Dept'); myTestObj = getNewGlobalRef (env : myTestObj); // Get the current "name" from the Test object // This creates a local reference to the Name object myString = getName (myTestObj); dsply (nameValue(myString)); // Change the name setName (myTestObj : 'RPG Department'); // Get the current "name" again. This will cause // access to the previous local reference to the old name // to be lost, making it impossible for this RPG // program to explicitly free the object. If the object // is never freed by this RPG program, Java could never // do garbage-collection on it, even though the old String // object is not needed any more. However, endObjGroup // will free the old reference, allowing garbage collection myString = getName (myTestObj); dsply (nameValue(myString)); // End the object group. This will free all local // references created since the previous beginObjGroup call. // This includes the two references created by the calls // to getName. endObjGroup (env); // Since the original Test object was made global, it can // still be used. setName (myTestObj : 'RPG Compiler Dept'); // The original Test object must be freed explicitly // Note: An alternative way to handle this situation // would be to use nested object groups, removing // the need to create a global reference // beginObjGroup ------------. // create myTestObj | // beginObjGroup ---------. | // ... | | // endObjGroup ---------' | // use myTestObj again | // endObjGroup ------------' freeGlobalRef (env : myTestObj); return; /end-free
ILE RPG では、例外が発生すると例外メッセージを出して知らせます。プログラムは明示的に例外をチェックする必要はありません。 代わりに、例外が発生した場合に制御を入手する例外処理プログラムをコーディングすることができます。ユーザーは、ユーザー自身の JNI 呼び出しを行なう時に JNI 例外を自分で処理する必要があるだけです。JNI 機能の呼び出しの結果、処理されない Java 例外が発生した場合、付随する例外メッセージはありません。その代わり、JNI プログラマーは JNI 機能を呼び出した後は毎回、例外が発生したかどうかをチェックする必要があります。これは、Java Exception オブジェクト (または JNI の値が 0 になる Java null オブジェクト) を戻す ExceptionOccurred JNI 機能を呼び出すことにより行ないます。いったん例外が発生したことを判別したら、 行なえる JNI 呼び出しは ExceptionClear と ExceptionDescribe のみです。ExceptionClear を実行した後、ユーザーは解放されて再度 JNI 呼び出しを行なえるようになります。ExceptionClear を呼び出す前に非例外 JNI 呼び出しを行なった場合は、例外は消失し、もはや詳細を知ることができなくなります。RPG は JNI 例外を必ず RPG 例外に変換します (その時実行中だった RPG 機能に応じて、RNX030x メッセージのいずれかが出されます)。
前述の JNI ラッパー・プロシージャーのユーザー独自のバージョンに、このようなタイプの例外処理コードを組み込むこともできます。
(C) Copyright IBM Corporation 1992, 2006. All Rights Reserved.