JavaプログラムからRPGプログラムの呼び出し

## 初めに
IBMi (AS/400)は優れたシステムであるが、企業における業務を記述するプログラムは手続型言語RPGやCOBOLで記述された資産が多くある。一方でオンプレ・オープンやクラウド環境を利用、Javaをはじめとするオブジェクト指向型言語で業務プログラムを記述するケースが多々あり、若手技術者はRPGやCOBOLでのプログラミングを忌避する傾向が強い。これによりRPGやCOBOLで記述された業務プログラムの保守・拡張を担う人材の減少が加速し、RPGやCOBOLで業務プログラムを記述している企業では、IBMiをモダナイズして業務システムの刷新に取り組む機会が多く見られる。
この記事では、RPGで記述されたプログラムを段階的にJavaプログラムにモダナイズしていく手順について解説を行う。

## クライアント・サーバシステムにおけるRPGプログラムの役割
IBMiをサーバサイドシステムと使用しRPGの業務プログラムとして動作させる手法は多々あるがここでは、クライアントシステムとIBMiをソケット通信で接続し、業務データを電文交換でやりとりしているシステムを一例として提示する。
#### ソケット電文交換でのIBMi使用例
![現行システム処理フロー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2334253/9096091a-1acb-86ef-a6cb-204426af45be.png)
- ⓪ソケットデーモンから起動されたソケットプロセスが要求電文をパラメータとして電文仕分けRPGプログラムを起動する。
- ①要求電文を電文仕様に基づきパースする
- ②電文IDから起動する業務RPGプログラムを特定する。
- ③要求電文の内容から業務RPGプログラムの入力パラメータを構築する。
- ④業務RPGプログラムの出力パラメータから応答電文を構築しソケットプロセスで制御を戻す。

## 既存RPGプログラムを活かした実行管理方式の検討
現在の業務システムではフロントサイドにWebアプリケーションをバックエンドにAPI（Webサービス）を配置する構造が主流となっている。今回もモダナイズ観点でこれに従うものとしている。バックエンドのAPI後方にある業務ロジック（RPG）はそのまま流用できる形を目指し実行管理方式の検討を行う。
#### ソケット電文交換に依存しない実行管理方式
![改訂版処理フロー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2334253/98988bf3-dabb-dc6e-329d-d97ffebb5179.png)
- ⓪UIは従来のVBFormからWebアプリに再構築する。
- ①バックエンドへの入口としてJavaでAPIを実装する
- ②ソケット電文方式で電文仕分けに利用していた電文仕分けRPGは破棄する
- ③IBMi向けのJavaライブラリを用いて業務RPGを直接Callする

## IBMi操作用のJavaライブラリ
前項で示したIBMi向けJavaライブラリーにはIBM社がフリーで提供している[IBM Toolbox for Java](https://www.ibm.com/docs/ja/was-nd/8.5.5?topic=variables-configuring-toolbox-java)を使用する。IBM Toolbox for JavaはIBMi上のプログラムを呼び出す[ProgramCall](https://www.ibm.com/docs/ja/i/7.3?topic=classes-programcall-class)の他にもライブラリ操作コマンドを代替するクラスやJDBCドライバが含まれる。JDBCドライバを経由するとJavaプログラムからSQLを用いてDB2 for IBMiのCRUDも可能になる。

## ProgramCallを用いた既存RPGプログラムの実行
ここではフロントエンドのJavaプログラムからIBMi上の業務RPGプログラムを呼び出すCodeサンプルを提示する。
#### ProgramCallクラスを用いたIBMiプログラム呼び出し
```
package jp.co.sample.pep;

import com.ibm.as400.access.AS400;
import com.ibm.as400.access.AS400Message;
import com.ibm.as400.access.ProgramCall;
import com.ibm.as400.access.ProgramParameter;

import jp.co.sample.parm.ParmListFJ2010WR;

public class test05 {

	public static void main(String[] args) {
		System.out.println("ProgramCall Test is started.");
		String targetsys = args[0];
		String user = args[1];
		String pass = args[2];
		
		@SuppressWarnings("deprecation")
		AS400 as400 = new AS400(targetsys,user,pass);
		try {
			ProgramCall pc = new ProgramCall(as400);
			String programName = "/QSYS.LIB/IBMYAMA.LIB/FJ2010WR.PGM";
			//パラメータリストクラスをインスタンス化
			ParmListFJ2010WR parmListInst = new ParmListFJ2010WR();
			
			/*
			入力パラメータ８０バイト
			 D W01PARMIN       DS
			 D  W01KINOIDI                    1
			 D  W01KOFLIDI                    1
			 D  W01KOKYCDI                   15
			 D  W01JTKMKBI                    1
			 D  W01MOTWBGI                   13
			 D  W01KIINKBI                    2
			 D  W01KIINBGI                   16
			 D  W01WARIKNI                    9
			 D  W01WARIKBI                    1
			 D  W01WARICDI                    3
			 D  W01JHSBCDI                    3
			 D  W01MIKTKBI                    1
			 D  W01UKTKBGI                   14
			 */
			parmListInst.setW01KINOIDI("1");
			parmListInst.setW01KOFLIDI("A");
			parmListInst.setW01KOKYCDI("あいうえおかX");
			parmListInst.setW01JTKMKBI("A");
			parmListInst.setW01MOTWBGI("1234567890123");
			parmListInst.setW01KIINKBI("AB");
			parmListInst.setW01KIINBGI("1234567890123456");
			parmListInst.setW01WARIKNI("ABCDEFGHI");
			parmListInst.setW01WARIKBI("1");
			parmListInst.setW01WARICDI("ABC");
			parmListInst.setW01JHSBCDI("123");
			parmListInst.setW01MIKTKBI("1");
			parmListInst.setW01UKTKBGI("12345678901234");
//			LDA設定
//			WSID
			parmListInst.setL01WSID("WS00000001");
//			一般ユーザ
//			parmListInst.setL01USER("GENUSR0001");
//			開発ユーザ
			parmListInst.setL01USER("AEVUSR0001");
//			デバッグ用待機時間
			parmListInst.setL01WAITTIME("0010");
			
			ProgramParameter[] parmlist = parmListInst.getParmlist();
			pc.setProgram(programName, parmlist);

			if (pc.run() != true) {
				// Report failure.
				System.out.println("Program failed!");
				// Show the messages.
				AS400Message[] messagelist = pc.getMessageList();
				for (int i = 0; i < messagelist.length; ++i) {
					// Show each message.
					System.out.println(messagelist[i]);
				}
			} else {
				/*
				出力パラメータ３２バイト
			     D W01PARMOT       DS
			     D  W01RTRNCDO                    2
			     D  W01UKTKBGO                   14
			     D  W01MKJUBGO                   10
			     D  W01JUDNBGO                    6
				 */
				System.out.println("outout param length is ->" + parmListInst.prepareW01PARMOT() + "<<");
				System.out.println("W01RTRNCDO is ->" + parmListInst.getW01RTRNCDO() + "<<");
				System.out.println("W01UKTKBGO is ->" + parmListInst.getW01UKTKBGO() + "<<");
				System.out.println("W01MKJUBGO is ->" + parmListInst.getW01MKJUBGO() + "<<");
				System.out.println("W01JUDNBGO is ->" + parmListInst.getW01JUDNBGO() + "<<");
			}
			System.out.println("ProgramCall Test is finshed.");
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			as400.disconnectAllServices();
		}
	}
}

```
#### 使用するパラメータクラス
```
package jp.co.sample.parm;

import java.util.ArrayList;

import com.ibm.as400.access.AS400Text;
import com.ibm.as400.access.ProgramParameter;

public class ParmListFJ2010WR extends BaseParameter {

	/*
	入力パラメータ８０バイト
	 D W01PARMIN       DS
	 D  W01KINOIDI                    1
	 D  W01KOFLIDI                    1
	 D  W01KOKYCDI                   15
	 D  W01JTKMKBI                    1
	 D  W01MOTWBGI                   13
	 D  W01KIINKBI                    2
	 D  W01KIINBGI                   16
	 D  W01WARIKNI                    9
	 D  W01WARIKBI                    1
	 D  W01WARICDI                    3
	 D  W01JHSBCDI                    3
	 D  W01MIKTKBI                    1
	 D  W01UKTKBGI                   14
	 */
	/*
	出力パラメータ３２バイト
	 D W01PARMOT       DS
	 D  W01RTRNCDO                    2
	 D  W01UKTKBGO                   14
	 D  W01MKJUBGO                   10
	 D  W01JUDNBGO                    6
	 */

	/*
	C     WPPRM01       PLIST
	C                   PARM                    W01PARMIN
	C                   PARM                    W01PARMOT
	
	
	/*
	C                   CALL      'FJ2010C'     WPPRM01                99
	*呼び出しエラーはリターンコード'80'セットのみ
	C   99              MOVEL     '80'          SNDA(01)
	C   99              Z-ADD     1             WPOCNT
	C   99              GOTO      $END
	 */

	// FJ2010C PLIST
	private ProgramParameter[] parmlist = new ProgramParameter[3];
	// 入力パラメータデータ構造 W01PARMIN
	private ArrayList<Byte> W01PARMIN = new ArrayList<Byte>();
	// LDAパラメータデータ構造 W01LDA
	private ArrayList<Byte> W01LDA = new ArrayList<Byte>();

	public ProgramParameter[] getParmlist() {
		byte[] temp = new byte[80];
		for (int i = 0; i < temp.length; i++) { // Byteからbyteに変換する
			temp[i] = W01PARMIN.get(i).byteValue();
		}
		byte[] temp2 = new byte[24];
		for (int i = 0; i < temp2.length; i++) { // Byteからbyteに変換する
			temp2[i] = W01LDA.get(i).byteValue();
		}	
		parmlist[0] = new ProgramParameter(temp);
		parmlist[1] = new ProgramParameter(32);
		parmlist[2] = new ProgramParameter(temp2);
		return parmlist;
	}

	// 入力パラメータデータ構造内の項目 W01PARMIN
	private String W01KINOIDI = "";
	private String W01KOFLIDI = "";
	private String W01KOKYCDI = "";
	private String W01JTKMKBI = "";
	private String W01MOTWBGI = "";
	private String W01KIINKBI = "";
	private String W01KIINBGI = "";
	private String W01WARIKNI = "";
	private String W01WARIKBI = "";
	private String W01WARICDI = "";
	private String W01JHSBCDI = "";
	private String W01MIKTKBI = "";
	private String W01UKTKBGI = "";
	// LDAパラメータデータ構造内の項目
	private String L01WSID = "";
	private String L01USER = "";
	private String L01WAITTIME = "";

	private int lenW01KINOIDI = 1;
	private int lenW01KOFLIDI = 1;
	private int lenW01KOKYCDI = 15;
	private int lenW01JTKMKBI = 1;
	private int lenW01MOTWBGI = 13;
	private int lenW01KIINKBI = 2;
	private int lenW01KIINBGI = 16;
	private int lenW01WARIKNI = 9;
	private int lenW01WARIKBI = 1;
	private int lenW01WARICDI = 3;
	private int lenW01JHSBCDI = 3;
	private int lenW01MIKTKBI = 1;
	private int lenW01UKTKBGI = 14;
	private int lenL01WSID= 10;
	private int lenL01USER= 10;
	private int lenL01WAITTIME= 4;

	AS400Text workText = new AS400Text(16);
	public void setL01WSID(String l01wsid) {
		L01WSID = l01wsid;
		byte[] bufByte = workText.toBytes(L01WSID);
		for (int i = 0; i < lenL01WSID; i++) {
			W01LDA.add(bufByte[i]);
		}

	}

	public void setL01USER(String l01user) {
		L01USER = l01user;
		byte[] bufByte = workText.toBytes(L01USER);
		for (int i = 0; i < lenL01USER; i++) {
			W01LDA.add(bufByte[i]);
		}

	}

	public void setL01WAITTIME(String l01waittime) {
		L01WAITTIME = l01waittime;
		byte[] bufByte = workText.toBytes(L01WAITTIME);
		for (int i = 0; i < lenL01WAITTIME; i++) {
			W01LDA.add(bufByte[i]);
		}

	}

	
	public void setW01KINOIDI(String w01kinoidi) {
		W01KINOIDI = w01kinoidi;
		byte[] bufByte = workText.toBytes(W01KINOIDI);
		for (int i = 0; i < lenW01KINOIDI; i++) {
			W01PARMIN.add(bufByte[i]);
		}

	}

	public void setW01KOFLIDI(String w01koflidi) {
		W01KOFLIDI = w01koflidi;
		byte[] bufByte = workText.toBytes(W01KOFLIDI);
		for (int i = 0; i < lenW01KOFLIDI; i++) {
			W01PARMIN.add(bufByte[i]);
		}
	}

	public void setW01KOKYCDI(String w01kokycdi) {
		W01KOKYCDI = w01kokycdi;
		byte[] bufByte = workText.toBytes(W01KOKYCDI);
		for (int i = 0; i < lenW01KOKYCDI; i++) {
			W01PARMIN.add(bufByte[i]);
		}
	}

	public void setW01JTKMKBI(String w01jtkmkbi) {
		W01JTKMKBI = w01jtkmkbi;
		byte[] bufByte = workText.toBytes(W01JTKMKBI);
		for (int i = 0; i < lenW01JTKMKBI; i++) {
			W01PARMIN.add(bufByte[i]);
		}
	}

	public void setW01MOTWBGI(String w01motwbgi) {
		W01MOTWBGI = w01motwbgi;
		byte[] bufByte = workText.toBytes(W01MOTWBGI);
		for (int i = 0; i < lenW01MOTWBGI; i++) {
			W01PARMIN.add(bufByte[i]);
		}
	}

	public void setW01KIINKBI(String w01kiinkbi) {
		W01KIINKBI = w01kiinkbi;
		byte[] bufByte = workText.toBytes(W01KIINKBI);
		for (int i = 0; i < lenW01KIINKBI; i++) {
			W01PARMIN.add(bufByte[i]);
		}
	}

	public void setW01KIINBGI(String w01kiinbgi) {
		W01KIINBGI = w01kiinbgi;
		byte[] bufByte = workText.toBytes(W01KIINBGI);
		for (int i = 0; i < lenW01KIINBGI; i++) {
			W01PARMIN.add(bufByte[i]);
		}
	}

	public void setW01WARIKNI(String w01warikni) {
		W01WARIKNI = w01warikni;
		byte[] bufByte = workText.toBytes(W01WARIKNI);
		for (int i = 0; i < lenW01WARIKNI; i++) {
			W01PARMIN.add(bufByte[i]);
		}
	}

	public void setW01WARIKBI(String w01warikbi) {
		W01WARIKBI = w01warikbi;
		byte[] bufByte = workText.toBytes(W01WARIKBI);
		for (int i = 0; i < lenW01WARIKBI; i++) {
			W01PARMIN.add(bufByte[i]);
		}
	}

	public void setW01WARICDI(String w01waricdi) {
		W01WARICDI = w01waricdi;
		byte[] bufByte = workText.toBytes(W01WARICDI);
		for (int i = 0; i < lenW01WARICDI; i++) {
			W01PARMIN.add(bufByte[i]);
		}
	}

	public void setW01JHSBCDI(String w01jhsbcdi) {
		W01JHSBCDI = w01jhsbcdi;
		byte[] bufByte = workText.toBytes(W01JHSBCDI);
		for (int i = 0; i < lenW01JHSBCDI; i++) {
			W01PARMIN.add(bufByte[i]);
		}
	}

	public void setW01MIKTKBI(String w01miktkbi) {
		W01MIKTKBI = w01miktkbi;
		byte[] bufByte = workText.toBytes(W01MIKTKBI);
		for (int i = 0; i < lenW01MIKTKBI; i++) {
			W01PARMIN.add(bufByte[i]);
		}
	}

	public void setW01UKTKBGI(String w01uktkbgi) {
		W01UKTKBGI = w01uktkbgi;
		byte[] bufByte = workText.toBytes(W01UKTKBGI);
		for (int i = 0; i < lenW01UKTKBGI; i++) {
			W01PARMIN.add(bufByte[i]);
		}
	}

	// 出力パラメータデータ構造内の項目 W01PARMOT
	byte[] W01PARMOT = new byte[32];

	public int prepareW01PARMOT() {

		W01PARMOT = parmlist[1].getOutputData();
		System.out.println("data.length is ->" + W01PARMOT.length);
		return W01PARMOT.length;
	}
	/*
	出力パラメータ３２バイト
	 D W01PARMOT       DS
	 D  W01RTRNCDO                    2
	 D  W01UKTKBGO                   14
	 D  W01MKJUBGO                   10
	 D  W01JUDNBGO                    6
	 */

	private String W01RTRNCDO = "";
	private String W01UKTKBGO = "";
	private String W01MKJUBGO = "";
	private String W01JUDNBGO = "";

	private int lenW01RTRNCDO = 2;
	private int lenW01UKTKBGO = 14;
	private int lenW01MKJUBGO = 10;
	private int lenW01JUDNBGO = 6;

	public String getW01RTRNCDO() {
		byte[] bufByte = new byte[lenW01RTRNCDO];
		for (int i = 0; i < lenW01RTRNCDO; i++) {
			bufByte[i] = W01PARMOT[i];
		}
		AS400Text workText2 = new AS400Text(lenW01RTRNCDO);

		String W01RTRNCDO = (String) workText2.toObject(bufByte);
		return W01RTRNCDO;
	}

	public String getW01UKTKBGO() {
		byte[] bufByte = new byte[lenW01UKTKBGO];
		for (int i = 0; i < lenW01UKTKBGO; i++) {
			bufByte[i] = W01PARMOT[i + lenW01RTRNCDO];
		}
		AS400Text workText2 = new AS400Text(lenW01UKTKBGO);

		String W01UKTKBGO = (String) workText2.toObject(bufByte);
		return W01UKTKBGO;
	}

	public String getW01MKJUBGO() {
		byte[] bufByte = new byte[lenW01MKJUBGO];
		for (int i = 0; i < lenW01MKJUBGO; i++) {
			bufByte[i] = W01PARMOT[i + lenW01RTRNCDO + lenW01UKTKBGO];
		}
		AS400Text workText2 = new AS400Text(lenW01MKJUBGO);

		String W01MKJUBGO = (String) workText2.toObject(bufByte);
		return W01MKJUBGO;
	}

	public String getW01JUDNBGO() {
		byte[] bufByte = new byte[lenW01JUDNBGO];
		for (int i = 0; i < lenW01JUDNBGO; i++) {
			bufByte[i] = W01PARMOT[i + lenW01RTRNCDO + lenW01UKTKBGO + lenW01MKJUBGO];
		}
		AS400Text workText2 = new AS400Text(lenW01JUDNBGO);

		String W01JUDNBGO = (String) workText2.toObject(bufByte);
		return W01JUDNBGO;
	}

}

```

## 終わりに

本記事においては、ソケット電文を用いたクライアントサーバー実行管理方式からWebアプリケーション実行管理方式へのモダナイズ手法としてWebサービスからProgramCallを経由したIBMiプログラム呼び出しについて言及しているが、この他にも以下のテーマが含まれている。

- 入出力パラメータのデータクラス化（電文仕分けPGMの記述から作成している）
- LDA（local data area）の扱い
- JOBA（ジョブ属性＝IBMi側の実行単位とその情報）の扱い
- バッチ動作しいているIBMi側プログラムのオンラインデバッグ

これらの内容については当記事の続編として今後記述したいと考えている。