Java を使用するためのその他の RPG コーディング

通常は 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 を参照してください。

複数のオブジェクトの同時解放を Java に通知

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
注:
このラッパーを呼び出すには、JNI 環境ポインター (下記の JNI 環境ポインターの入手で説明) が必要です。

一時オブジェクトの使用が終了したことを Java に伝える

Java コンストラクターを使用してオブジェクトを作成した場合、またはユーザーにオブジェクトを戻す Java メソッドを呼び出した場合、このオブジェクトは、Java のガーベッジ・コレクションがもうユーザーがそのオブジェクトを必要としていないと知ったときに破棄されるまでの間だけ、使用可能であることになります。 これは、ネイティブ・メソッド (java によって呼ばれる) が戻るときにネイティブ・メソッドのために発生しますが、そうでない場合は、オブジェクトを必要としなくなった Java に明示的に通知しない限り、決して発生しません。 これは、RPG ラッパー・プロシージャー freeLocalRef を呼び出すことによって行ないます。

CALLP  freeLocalRef (JNIEnv_P : string);

図 87 に、freeLocalRef のサンプル・ソース・コードを示します。

図 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
     
注:
このラッパーを呼び出すには、JNI 環境ポインター (下記の JNI 環境ポインターの入手で説明) が必要です。

オブジェクトを永続にしたいことを Java に伝える

ユーザーにパラメーターとして渡されたか、または Java メソッドあるいはコンストラクターを呼び出すことによって作成されたか、いずれかの Java オブジェクトに対して、ユーザーが参照を行なう場合で、かつそのオブジェクトをユーザーのネイティブ・メソッドが戻った後も使用したい場合は、Java に対してそのオブジェクトを永続または「グローバル」にしたいということを伝える必要があります。これは、RPG ラッパー・プロシージャー getNewGlobalRef を呼び出し、結果をグローバル変数に保管することによって行ないます。

EVAL   globalString = getNewGlobalRef (JNIENV_P : string);

図 88 に、getNewGlobalRef のサンプル・ソース・コードを示します。

図 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
注:
このラッパーを呼び出すには、JNI 環境ポインター (下記の JNI 環境ポインターの入手で説明) が必要です。

永続オブジェクトの使用が終了したことを Java に伝える

グローバル参照を作成した場合で、そのオブジェクトが既に必要ではなくなったことが わかっている場合は、ユーザー側に関しては、このオブジェクトを次回 Java が ガーベッジ・コレクションを行なう時点で破棄してよい、ということを Java に 対して伝える必要があります。 (オブジェクトは、グローバル参照の対象でなくなり、かつ Java それ自身の中で他から参照されなくなった場合にのみ、破棄されます。) Java に、ユーザーがもうオブジェクトへの参照を必要としなくなったことを伝えるためには、RPG ラッパー・プロシージャー freeGlobalRef を呼び出します。

CALLP  freeGlobalRef (JNIEnv_P : globalString);

図 89 に、freeGlobalRef のサンプル・ソース・コードを示します。

図 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
注:
このラッパーを呼び出すには、JNI 環境ポインター (下記の JNI 環境ポインターの入手で説明) が必要です。

Java 仮想マシン (JVM) の作成

ユーザーの RPG コードが Java メソッドを呼び出す準備ができた時点でまだ JVM が作成されていない場合は、RPG は、デフォルトのクラスパスに加えてユーザーの CLASSPATH 環境変数の中のクラスパスを使用して、ユーザーの代わりに JVM を作成します。ただし、ユーザー自身が JVM を作成したい場合は、図 90 の最後の部分にあるこのコーディングの例を参照することができます。

JNI 環境ポインターの入手

何らかの JNI 機能を呼び出す必要がある場合は、QSYSINC/QRPGLESRC からの /COPY ファイル JNI を使用します。 JNI 機能のほとんどは、プロシージャー・ポインターを介して呼び出されます。プロシージャー・ポインターは、「JNI 環境ポインター」と呼ばれるポインターにそれ自身が基づいているデータ構造の一部です。このポインターは JNI /COPY ファイルの中で JNIEnv_P と呼ばれています。 このポインターを入手するには、JNI ラッパー・プロシージャー getJniEnv を呼び出します。

EVAL   JNIEnv_P = getJniEnv();

図 90 に、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
図 91. JNI 関数用のラッパーの使用
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

JNI 例外の処理

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 ラッパー・プロシージャーのユーザー独自のバージョンに、このようなタイプの例外処理コードを組み込むこともできます。