拡張 JNI コーディング

RPG IV コンパイラーの Java(TM) メソッド呼び出しのサポート、および RPG ネイティブ・メソッドの作成のサポートでは、ほとんどすべての JNI コーディングは、RPG プログラマーからは見えません。ただし、RPG のサポートが最も効果的であるとは限りません。例えば、RPG は 呼び出しの時およびネイティブ・メソッドの入り口と出口において必ず、RPG と Java との間の配列の変換を行ないますが、パフォーマンスを向上させるにはユーザー自身で配列の変換を行なった方がよい場合もあります。

RPG のサポートは、ユーザーに Java メソッドへのアクセスを提供しているに過ぎません。 クラスの中のフィールドにアクセスしたい場合は Java クラスに対して「入手」メソッドと「設定」メソッドを追加するか、または JNI コーディングをすることが必要になります (Java クラスの中のフィールドへのアクセス を参照)。

図 92 は、RPG における JNI 呼び出しの例です。

図 92. RPG における JNI 呼び出し
 /COPY JNI
D objectId        s                   like(jobject)
D methodId        s                   like(jmethodID)
D string          s                   like(jstring)
D parms           s                   like(jvalue) dim(2)

  /free
     jvalue_P = %addr(parms(1));
     jvalue.i = 10;
     jvalue_P = %addr(parms(2));
     jvalue.l = string;
     CallVoidMethodA (JNIEnv_P : objectId : methodId : parms);
  /end-free

ポインター JNIEnv_P およびポインター jvalue_P は JNI /COPY ファイルの中で定義されていることに注意してください。

Java 文字データの変換

Java では、文字データは EBCDIC ではなく ASCII であるため、FindClass などの JNI 機能を呼び出すには、クラス名、メソッド名、およびフィールド名は必ず ASCII にしておく必要があります。Java から来る文字データも ASCII なので、これをユーザーの RPG プログラムの中で使用するには、おそらく EBCDIC への変換をすることになります。RPG コンパイラーはこれらの変換をユーザーに代わって行ないますが、ユーザー自身が JNI 呼び出しを行なう場合は ASCII と EBSDIC 間の変換を行なう必要があります。

Java クラスの中のフィールドへのアクセス

RPG がサポートするのは Java メソッドの 呼び出しのみです。Java フィールドへの アクセスはサポートしません。通常、フィールドは「get」メソッドおよび「set」メソッドを通じて アクセスされますが、JNI 呼び出しを使用してフィールドにアクセスすることも可能です。ここでは、 Java クラスまたはオブジェクトのフィールドへのアクセスに必要な JNI 呼び出しの例を示します。

注:
この例は JNI を使用する例となることを目的としたものです。 「get」メソッドや「set」メソッドを使用するよりも、フィールドに直接にアクセスすることを推奨する、という意味ではありません。
図 93. Java クラスおよびオブジェクトのフィールドにアクセスするための JNI の使用
 *------------------------------------------------------------------
 * この例は、クラスまたはオブジェクトのフィールドにアクセスするための
 * JNI の使用法を示しています。
 *
 * このプログラムは Rectangle オブジェクトを作成し、JNI 呼び出しを
 * 使用して、width 変数と height 変数に直接アクセスします。
 *
 * この特定の例においては、getWidth()、getHeight、
 * setWidth() および setHeight() メソッドを使用して
 * これらのフィールドにアクセスし、JNI 呼び出しの使用は避けます。
 *------------------------------------------------------------------
H THREAD(*SERIALIZE)
 /DEFINE JNI_COPY_FIELD_FUNCTIONS
 /COPY JNI
 /COPY JNIRPG_PR

 *------------------------------------------------------------------
 * JAVA クラスおよびメソッド
 *------------------------------------------------------------------
D Rectangle       C                   'java.awt.Rectangle'
D NewRectangle    PR              O   EXTPROC(*JAVA
D                                           : Rectangle
D                                           : *CONSTRUCTOR)
D     x                         10I 0 VALUE
D     y                         10I 0 VALUE
D     width                     10I 0 VALUE
D     height                    10I 0 VALUE
 *------------------------------------------------------------------
 * JAVA 名の ASCII 表記を用いた定数
 *------------------------------------------------------------------
 * これらの値を判別する 1 つの方法は、文字値を UCS-2 に変換するために
 * %UCS2 を使用して、結果をデバッガーの中で
 * 16 進で表示するという方法です。
 *
 * ASCII 値は UCS-2 文字に 2 バイトおきに入っています。
 *
 *     例えば、%UCS2('abc') = X'006100620063'
 *                                     --  --  --
 *     'abc' の ASCII 表記は X'616263' です。
 *------------------------------------------------------------------
D ASCII_I         C                   x'49'
D ASCII_x         C                   x'78'
D ASCII_y         C                   x'79'
D ASCII_width     C                   X'7769647468'
D ASCII_height    C                   X'686569676874'
  
 * JNI はスラッシュを区切り文字として使用するため、これは
 *「java/awt/Rectangle」であって「java.awt.Rectangle」ではないことに注意。
D ASCII_Rectangle...
D                 C                   X'6A6176612F6177742F52656-
D                                     374616E676C65'

 *------------------------------------------------------------------
 * 取り消し処理
 *------------------------------------------------------------------
D EnableCanHdlr   PR                  EXTPROC('CEERTX')
D   Handler                       *   CONST PROCPTR
D   CommArea                      *   CONST OPTIONS(*OMIT)
D   Feedback                    12A   OPTIONS(*OMIT)
D CanHdlr         PR
D   CommArea                      *   CONST

 *------------------------------------------------------------------
 * 変数およびプロシージャー
 *------------------------------------------------------------------
D rect            s               O   CLASS(*JAVA : Rectangle)
D x               S             10I 0
D y               S             10I 0
D rectClass       S                   LIKE(jclass)
D fieldId         S                   LIKE(jfieldID)
D msg             S             52A
D Cleanup         PR

 *------------------------------------------------------------------
 * 取り消し処理プログラムを使用可能にし終結処理が完了したことを確認
 *------------------------------------------------------------------
C                   CALLP     EnableCanHdlr (%PADDR(CanHdlr)
C                                          : *OMIT : *OMIT)

 *------------------------------------------------------------------
 * x,y 座標 (5, 15) を使用して、幅 100 で高さが 200 の
 * 新しい Rectangle を作成します。
 *------------------------------------------------------------------
C                   EVAL      rect = NewRectangle (5 : 15 : 100 : 200)

 *------------------------------------------------------------------
 * Rectangle のフィールドにアクセスするための JNI 機能を呼び出す準備
 *------------------------------------------------------------------
C                   EVAL      JNIEnv_P = getJniEnv ()
C                   EVAL      rectClass = FindClass (JNIEnv_P
C                                                  : ASCII_Rectangle)
 *------------------------------------------------------------------
 * Rectangle の幅および高さを検索するための JNI 機能の呼び出し
 *------------------------------------------------------------------
C                   eval      fieldId = GetFieldID (JNIEnv_P
C                                                 : rectClass
C                                                 : ASCII_width
C                                                 : ASCII_I)
C                   eval      width = GetIntField (JNIEnv_P
C                                                  : rect
C                                                  : fieldId)
C                   eval      fieldId = GetFieldID (JNIEnv_P
C                                                 : rectClass
C                                                 : ASCII_height
C                                                 : ASCII_I)
C                   eval      height = GetIntField (JNIEnv_P
C                                                  : rect
C                                                  : fieldId)
C                   eval      msg = 'The rectangle has dimensions ('
C                                 + %trim(%editc(width : '1'))
C                                 + ', '
C                                 + %trim(%editc(height : '1'))
C                                 + ')'
C     msg           dsply

 *------------------------------------------------------------------
 * Cleanup プロシージャーを呼び出します。
 *------------------------------------------------------------------
C                   callp     Cleanup()
C                   eval      *INLR = '1'

 *------------------------------------------------------------------
 * Cleanup。* - 必要であればオブジェクトを解放します。
 *------------------------------------------------------------------
P Cleanup         B
C                   if        rect <> *NULL and
C                             JNIEnv_P <> *NULL
C                   callp     DeleteLocalRef(JNIEnv_P : rect)
C                   endif
C                   eval      rect = *NULL
C                   eval      JNIEnv_P = *NULL
P Cleanup         E

 *------------------------------------------------------------------
 * 取り消し処理プログラム。終結処理が完了したことを確認。
 *------------------------------------------------------------------
P CanHdlr         B
D CanHdlr         PI
D   CommArea                      *   CONST
C                   callp     Cleanup()
P CanHdlr         E

RPG *JAVA プロトタイプではなく JNI を使用する Java メソッドの呼び出し

最初の 3 つのパラメーターは、必ず次と同じになります。

  1. JNI 環境ポインター
  2. オブジェクト (インスタンス・メソッドの場合) またはクラス (静的メソッドの場合)
  3. メソッド

メソッド固有のパラメーターは、これら 3 つのパラメーターの後に、3 つの異なる方法のいずれかでコーディングされます。例えば、メソッドが値を戻さない場合 (戻りタイプは「void」)。

CallVoidMethod:
同じメソッドを何度も呼び出そうとしている場合はこの方法を選択します。 これはメソッドを非常に容易に呼び出せるからです。この方法では、パラメーターは普通に渡されることを想定しています。この JNI 機能を呼び出すには、RPG は CallVoidMethod プロトタイプを JNI/COPY ファイルからコピーして、追加のパラメーターをコーディングします。この関数は、少なくとも 1 つのパラメーターを OPTIONS(*NOPASS) を指定してコード化する必要があります。メソッド・パラメーターをオプションにしない場合は、余分の "dummy" パラメーターを OPTIONS(*NOPASS) を指定して追加します。 例えば、メソッド
void mymethod (int len, String str) の場合、
CallVoidMethod 用に以下のプロトタイプをコード化することができます。
図 94. CallVoidMethod を呼び出すサンプル RPG コーディング
D CallMyMethod    PR                  EXTPROC(*CWIDEN
D                                     : JNINativeInterface.
D                                       CallVoidMethod_P)
D env                                 LIKE(JNIEnv_P) VALUE
D obj                                 LIKE(jobject) VALUE
D methodID                            LIKE(jmethodID) VALUE
D len                                 LIKE(jint) VALUE
D str                                 LIKE(jstring) CONST
D dummy                          1a   OPTIONS (*NOPASS)

...

CallMyMethod (JNIEnv_P : objectId : methodId : 10 : string);       
CallVoidMethodA:
メソッドを呼び出す場合に別のプロトタイプを作成したくない場合には、この方法を選択します。 この方法では、配列の各要素が 1 つのパラメーターを保持する、jvalue 構造の配列を想定しています。前述の 図 92 はこの例です。
CallVoidMethodV:
これは RPG コードの中では使用してはなりません。これは C 構成を想定したもので、RPG でコーディングするのはきわめて不向きです。

実際にどの機能を呼び出すかは、戻り値のタイプによって決まります。例えば、メソッドが整数を戻す場合であれば、 CallIntMethodA を使用することになります。これらの機能のクラスおよび methodID パラメーターを入手するには、FindClass と GetMethodID または GetStaticMethodID を使用します。

注:
JNI を直接に呼び出す場合、クラス名はピリオド (.) ではなくスラッシュ (/) を区切り文字として使用して指定する必要があります。例えば、「java/lang/String」であって、「java.lang.String」ではありません。