JZOSを使ってJavaからデータセットにアクセスしてみる

# 概要
z/OS環境（IBMメインフレーム環境）におけるファイルはデータセット(data set)と呼ばれています。データセットはLinux,Unix,Windowsのファイルとはちょっと違います（**※1**）。
以下にデータセットの特徴とJava言語によるデータセット入出力処理の実装サンプルを示します。

**※1**　z/OS環境にはTSOやJCLで操作する環境とは別にUSSと呼ばれるUnix環境もあります。USS環境のファイルはLinux,Unix,Windowsのファイルと同じようにJavaの標準APIでアクセス可能です。


# データセットの特徴
データセットには以下のような特徴があります。

1. 入力・出力いずれの場合も、オープンの前にまずアロケート（プロセスに対するデータセットの関連付け）をしなければならない。
2. 新規データセットの作成時には、レコード・フォーマット、論理レコード長、ブロックサイズ（論理レコードをいくつかまとめたものをブロックといいます）、データセット編成等の属性を指定してアロケートしなければならない。
3. 入力・出力いずれの場合でも、クローズの後にフリー（アロケートの逆でデータセットをプロセスから開放する）をしなければならない。
4. レコードの区切り位置は改行文字ではなくレコード長により識別される。

上記のような特徴を持つデータセットは、Java標準API(java.ioパッケージやjava.nioパッケージ)で入出力処理を実装することはできません。
では、どうするかというと、**Java Batch Launcher and Toolkit for z/OS（以降、JZOSと表記）**を利用します。
JZOSは、z/OS環境で実行するJavaアプリケーションの開発を支援するツールキットで、IBM Java for z/OSに含まれています（JZOSの概要については参考リンク[JZOS Batch Launcher and Toolkit](https://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.zsecurity.80.doc/zsecurity-component/jzos.html "参考リンク:JZOS Batch Launcher and Toolkit")を参照）。

以下にデータセットからの入力処理を実装したサンプルJavaアプリケーションおよびデータセットへの出力処理を実装したサンプルJavaアプリケーションをそれぞれ示します（**※2**）。

**※2**　データセットには様々な種類がありますが、以降ではWindowsファイルやUnixファイルに類似していて最も単純な構造の順次編成方式のデータセットに対象を絞ってサンプルを示しています。

# サンプル・プログラム（データセットからの入力処理）
以下のサンプル・プログラム(DSRead.java)は、データセット`'USER01.TEST.DATA'`からレコードを読み込み標準出力に書き出します。データは`UTF-8`でエンコーディングされているものとします。

```DSRead.java
package sample;

import com.ibm.jzos.RecordReader;
import com.ibm.jzos.ZFile;

public class DSRead {
	public void main(String args[]) throws Exception {
		String ddName = ZFile.allocDummyDDName();
		String dsName = "USER01.TEST.DATA";

		RecordReader reader = null;
		int x = 0;

		try {
			ZFile.bpxwdyn("alloc fi("+ddName+") da("+dsName+") shr reuse");
			reader = RecordReader.newReaderForDD(ddName);
			byte[] recordBuf = new byte[reader.getLrecl()];
			while ((reader.read(recordBuf)) >= 0) {
				String rec = new String(recordBuf, "UTF-8");
				System.out.println(rec);
				x++;
			}
			System.out.println("record count : "+x);
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			ZFile.bpxwdyn("free fi("+ddName+")");
		}
	}
}
```
**ここがポイント!**

- データセットのアロケートとフリーは`ZFile#bpxwdyn`メソッドで行います。
    - このメソッドはダイナミック・アロケーション(プログラムから動的にデータセットをアロケーションすること)をするために[BPXWDYN](https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.2.0/com.ibm.zos.v2r2.bpxb600/wdyn.htm "参考リンク:BPXWDYN")と呼ばれるコマンド・インターフェースをコールします。実行したいコマンド文字列（`"alloc ..."`, `"free ..."`）を引数に指定します。
- allocコマンド(**※3**)とfreeコマンドには、DD名を指定します。DD名はプログラム内でデータセットを識別する論理名になります。`ZFile#allocDummyDDName`メソッドでユニークなDD名を生成しています。
- ファイルからデータ・レコードを読み込む処理は`RecordReader`で行います。
    - `RecordReader`のインスタンスは、前述のDD名を指定してファクトリー･メソッド`RecordReader#newReaderForDD`をコールすることによって取得します。APIの詳細は参考リンクの[JZOS API（Javadoc)](http://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.zsecurity.api.80.doc/com.ibm.jzos/index.html "参考リンク:JZOS API（Javadoc)")を参照してください。

**※3**　`alloc`は`allocate`の省略表記です。allocateコマンドの具体例は参考リンク[allocateコマンドの例](http://www.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.ikjc500/ikj2l2_ALLOCATE_command_examples.htm
 "参考リンク:allocateコマンドの例")を参照してください。

# サンプル・プログラム（データセットへの出力処理）
以下のサンプル・プログラム(DSWrite.java)は、既に存在しているデータセット'USER01.TEST.DATA'に上書きモードでレコードを書き出します。データセット'USER01.TEST.DATA'は、固定長で論理レコード長が80バイトの編成とし、`getTestData`メソッドで作った3行のレコードを出力します。データはUTF-8でエンコーディングして出力するものとします。

```DSWrite.java
package sample;

import com.ibm.jzos.RecordWriter;
import com.ibm.jzos.ZFile;

public class DSWrite {
	public void main(String args[]) throws Exception {
		String ddName = ZFile.allocDummyDDName();
		String dsName = "USER01.TEST.DATA";

		RecordWriter writer = null;
		int x = 0;

		try {
			ZFile.bpxwdyn("alloc fi("+ddName+") da("+dsName+") old reuse");
			writer = RecordWriter.newWriterForDD(ddName);
			int lrecl = writer.getLrecl();
			String records[] = getTestData();
			for (String record : records) {
				writer.write(record.getBytes("UTF-8"), 0, lrecl);
				x++;
			}
			System.out.println("record count : "+x);
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			if (writer != null) {
				try {
					writer.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			ZFile.bpxwdyn("free fi("+ddName+")");
		}
	}
	private String[] getTestData() {
		String[] records = new String[3];
		records[0] = String.format("%-80s", "AAAAAAAAAAAAAAAAAAAA");
		records[1] = String.format("%-80s", "BBBBBBBBBBBBBBBBBBBB");
		records[2] = String.format("%-80s", "CCCCCCCCCCCCCCCCCCCC");
		return records;

	}
}
```
**ここがポイント!**

- データセットのアロケートとフリーについては上記サンプル`DSRead.java`とほぼ同じです。
    - 唯一異なるのは、allocコマンドのDISPパラメーターに、shrではなくoldを指定している点です。oldを指定することによってデータセットを排他的にアクセスできます。
- ファイルにデータ・レコードを書き出す処理は`RecordWriter`で行います。
    - `RecordWrite`のインスタンスは、DD名を指定してファクトリー･メソッド`RecordWriter#newWriterForDD`をコールすることによって取得します。APIの詳細は参考リンクの[JZOS API（Javadoc)](http://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.zsecurity.api.80.doc/com.ibm.jzos/index.html "JZOS API（Javadoc)")を参照してください。


# 参考リンク
**JZOS Batch Launcher and Toolkit**
https://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.zsecurity.80.doc/zsecurity-component/jzos.html

**JZOS API (Javadoc)**
http://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.zsecurity.api.80.doc/com.ibm.jzos/index.html

**BPXWDYN**
https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.2.0/com.ibm.zos.v2r2.bpxb600/wdyn.htm

**allocateコマンドの例**
http://www.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.ikjc500/ikj2l2_ALLOCATE_command_examples.htm