概要
JZOS[参考リンク 1]を使うと、z/OS環境(IBMメインフレーム環境)で、データセットの入出力を行うJavaプログラムを開発することができます[参考リンク 6 ]。シングル・スレッド環境で動くJavaアプリケーションであれば、データセットの排他制御はOSに任せておけば問題ありません。データセットのアロケーション時に指定するDISPパラメーター(SHR/OLD)によって下表のように排他制御されますので、アプリケーションとして特別な制御ロジックを実装する必要はありません。この場合の排他制御はプロセス間の排他制御であり、COBOL言語やPL/I言語で書かれたバッチ・プログラムをJCLで実行する際の排他制御と同じです(下記<COBOLバッチにおけるデータセットの排他制御>参照)。
新規要求\実行中処理 | 読み取り中(SHR) | 書き込み中(OLD) |
---|---|---|
読み取り(SHR) | ○ | × (注1) |
書き込み(OLD) | × (注1) | × (注1) |
注1 JCLの場合は、アクセス要求が競合すると新規要求は待ち状態になり、実行中の処理が終了してデータセットが開放されるとアクセス可能になるが、BPXWDYNによるダイナミックアロケーションの場合は即時エラーになる。なお、書き込み時にDISP=OLD、読み取り時にDISP=SHRを指定するのは、JCLで排他制御をする際の慣習であり、DISP=SHRで書き込み処理したとしても、それ自体が原因でエラーが起きるわけではない。 |
<COBOLバッチにおけるデータセットの排他制御>
下図のようにジョブ#1のCOBOLバッチ#1がDISP=OLDで書き込み中のデータセットに対して、ジョブ#2のCOBOLバッチ#2がDISP=OLDでアクセスを試みると、OSの排他制御により、COBOLバッチ#2はデータセットが開放されるまで待たされます。
ところが、マルチスレッド環境のJavaプログラムでは、排他制御を全てOSまかせにすることはできません。以下にマルチスレッドJava環境でデータセットを扱う際のアクセス競合関連の課題とJZOS APIによる解決方法をサンプルを交えて説明します。
マルチスレッドJava環境での課題
シングル・スレッド環境のJavaプログラムからデータセットにアクセスする場合は、競合に関して特別な対応は不要ですが、WebSphere Application Server for z/OS(以降WAS for z/OSと表記)で動くサーバー・サイドJavaアプリケーションからデータセットにアクセスする場合は考慮が必要です。WAS for z/OS環境は、マルチスレッド環境(Java EE環境)だからです。前述のOSによる排他制御は、複数プロセス間で同一データセットにアクセスする場合の話であって、同一プロセス内の複数スレッドから同一データセットにアクセスする場合は、アプリケーションの責任で排他制御ロジックを実装してデータの破壊を防ぐ必要があります(下記 <WAS for z/OS上のJavaバッチにおけるデータセット排他制御の課題> 参照)。
<WAS for z/OS上のJavaバッチにおけるデータセット排他制御の課題>
下図のJavaバッチ#1とJavaバッチ#2の排他制御は有効に機能しますが、Javaバッチ#1とJavaバッチ#3のような同一アドレス空間(Unix/Windowのプロセスに相当)内の複数スレッド間の排他制御はアプリケーションの責任で実装する必要があります。
com.ibm.jzos.Enqueueクラスで排他制御ロジックを実装
では具体的にどうすれば排他制御を実装できるかというと、これまたJZOSのAPI(com.ibm.jzos.Enqueueクラスが提供するメソッド[参考リンク 2]で実現できてしまいます。
以下にサンプル・プログラムEnqTest.java
を示します。
実装の要点を伝えるため、下記サンプルはシンプルなスタンドアロンJavaアプリケーションの形態を採っていますが、mainメソッドに記述されたロジックは、Java EEアプリケーションでもほぼそのまま使える内容です(注2)。
package sample;
import com.ibm.jzos.ZFile;
import com.ibm.jzos.Enqueue;
import javax.xml.bind.DatatypeConverter;
public class EnqTest {
public static void main(String args[]) throws Exception {
String qname = "TEST_Q01";
String dsName = "USER001.TEST.DATA1";
Enqueue enq = new Enqueue(qname, dsName);
enq.setScope(Enqueue.ISGENQ_SCOPE_SYSTEM);
enq.setControl(Enqueue.ISGENQ_CONTROL_SHARED); // <- 読み込み処理の場合
//enq.setControl(Enqueue.ISGENQ_CONTROL_EXCLUSIVE); // <- 書き込み処理の場合
enq.setContentionActWait();
try{
enq.obtain();
byte[] token = enq.getEnqToken();
System.out.println("token:"+DatatypeConverter.printHexBinary(token));
//ここにデータセット入出力処理を記述
} catch(Throwable t) {
t.printStackTrace();
} finally {
if (enq.getEnqToken() != null) {
enq.release();
}
}
}
}
ここがポイント!
以下にサンプル・プログラムのポイントを説明します。上記サンプルは排他制御にフォーカスしているため、データセットの入出力処理については割愛しています。データセットの入出力処理の具体的な内容について知りたい方は、「参考リンク 6. JZOSを使ってJavaからデータセットにアクセスしてみる」で確認していただければと思います。
- 排他制御は、JZOS提供のcom.ibm.jzos.Enqueueクラスを利用して実装します**(注3)**。
- コンストラクターにキュー名とリソース名を指定してEnqueueオブジェクト生成します。
- キュー名には8文字以内の名前を指定します。
- リソース名は1バイト~255バイトの名前を指定します。
- Enqueue#setControlメソッドで下記いずれかのスコープを設定します。指定したスコープと上記キュー名+リソース名で排他制御対象リソースを識別し、エンキュー/デキューします**(注2)**。
- Enqueue.ISGENQ_SCOPE_STEP アドレス・スペース内
- Enqueue.ISGENQ_SCOPE_SYSTEM 単一システム内(デフォルト)
- Enqueue.ISGENQ_SCOPE_SYSTEMS シスプレックス全体
- Enqueue.ISGENQ_SCOPE_SYSSPLEX シスプレックス全体(SYSTEMSと同じ)
- Enqueue#setControlメソッドで下記いずれかの排他制御モードを指定します。
- Enqueue.ISGENQ_CONTROL_SHARED 共有モード(読み込み処理の場合に指定)
- Enqueue.ISGENQ_CONTROL_EXCLUSIVE 排他モード(書き込み処理の場合に指定)
- リソース競合発生時に待たせる場合は、Enqueue#setContentionActWaitをコールします。
- Enqueue#obtainメソッドでエンキューします。
- Enqueue#releaseメソッドでデキューします。
注2)
上記サンプルは、スタンドアロンJavaアプリケーションとして動かすために、スコープ指定をEnqueue.ISGENQ_SCOPE_SYSTEMにしていますが、WAS for z/OS環境で同一JVM内の複数スレッド間の競合を制御する場合は、Enqueue.ISGENQ_STEPをスコープに指定します。
注3)
APIの詳細については参考リンク2を参照してください。また、当該APIがコールするシステム・サービスについては、参考リンク3、参考リンク4、参考リンク5を参照してください。
参考リンク
1. 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
2. JZOS API (Javadoc)
http://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.zsecurity.api.80.doc/com.ibm.jzos/index.htm
com.ibm.jzos.EnqueueクラスのJavadoc
https://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.zsecurity.api.80.doc/com.ibm.jzos/com/ibm/jzos/Enqueue.html
3. グローバル・リソース逐次化
https://www.ibm.com/support/knowledgecenter/ja/SSLTBW_2.3.0/com.ibm.zos.v2r3.ieag400/comm.htm
4. ISGENQ — グローバル・リソース逐次化 ENQ サービス
https://www.ibm.com/support/knowledgecenter/ja/SSLTBW_2.3.0/com.ibm.zos.v2r3.ieaa900/isgenq.htm
5. ISGENQ — Global resource serialization ENQ service(上記の英文ページ)
https://www.ibm.com/support/knowledgecenter/ja/SSLTBW_2.3.0/com.ibm.zos.v2r3.ieaa200/isgenq.htm
6. JZOSを使ってJavaからデータセットにアクセスしてみる
https://qiita.com/tsunogai/items/8f75ce6a2f52c6c61ff5