z/OSのバッチはJCL(Job Control Language)でジョブを組み、処理本体をJavaで書くことができます。JCLからJavaを起動する代表的な方法はJZOS Batch LauncherとBPXBATCHで、本番の定常ジョブにはJZOSを使います。この記事では、JCLの基本の書き方を押さえたうえで、IBMが配布するJZOSのサンプルJCL(Semeru Runtime 21版)を読み解き、STDENVでの環境変数の設定、Javaの終了コードをジョブのリターンコードに対応させる方法、DD文で割り当てたデータセットをJavaから読む方法までを順に示します。Java 8とSemeru 17の標準サポートが2026年9月30日に終わったことを受けた版の選び方も扱います。
まとめ:z/OS JCLでJavaを動かす要点
- 本番のバッチはJZOS Batch Launcherを使います。Javaの
System.exit(n)の値がそのままステップの条件コードになり、JCLのIFやCONDで後続を分けられます。 - JZOSは3つの部品(ロードモジュール
JVMLDM21・プロシージャJVMPRC21・サンプルJCLJVMJCL21)で動きます。数字はSemeruの版に対応しますが、Semeru 11だけはJVMLDM16という名前です。 - 環境変数は
STDENVDDのシェルスクリプトでexportします。JZOSのSTDENVは非ログインシェルで動くため、/etc/profileの設定が必要な場合は明示的に読み込みます。 - JZOS自身が使う戻り値は100〜102です。Java側の終了コードにこの範囲を使わないでください。
- JavaからはJZOS Toolkitの
FileFactory.newBufferedReader("//DD:INPUT")のように、JCLで割り当てたDDを直接読めます。 - Java 8(64ビット)とSemeru 17は2026年9月30日に標準サポートが終わりました。新規のジョブはSemeru 21以降で組み、既存資産は移行計画を立てます。
以下、JCLの基本から順に説明します。
z/OS JCLの基本:JOB・EXEC・DDの3文とジョブの流れ
JCLは、z/OSにバッチジョブの内容を伝える制御文です。1つのジョブは、ジョブ全体を宣言するJOB文、実行するプログラムやプロシージャを指定するEXEC文、プログラムが使うデータセットを割り当てるDD文の3種類で書きます。各行は // で始まり、そのあとに名前と文の種類を続けます。
//MYJOB JOB (ACCT),'BATCH SAMPLE',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//STEP1 EXEC PGM=IEFBR14
//DD1 DD DSN=MY.INPUT.DATA,DISP=SHR
投入したジョブはJES(Job Entry Subsystem)が受け付け、イニシエーターがステップ単位で実行します。ステップの結果は条件コード(リターンコード、RC)で残り、後続のステップは COND パラメーターか IF/THEN/ELSE/ENDIF 文でRCを見て実行するかどうかを決めます。COND は「条件が真ならそのステップを飛ばす」という逆向きの書き方なので、新しく書くジョブでは読みやすい IF 文をおすすめします。
プロシージャ(PROC)によるJava起動手順の共通化
何度も使うステップは、カタログ式プロシージャ(PROC)としてPROCLIBに登録し、EXEC PROC=名前 で呼び出します。PROCの中の値は JAVACLS= のようなキーワードで上書きできます。JZOSはまさにこの仕組みで提供されていて、IBMのサンプルPROC JVMPRC21 を呼び、実行するJavaクラスを JAVACLS に渡すのが基本形です。PROCが標準のPROCLIBに無い場合は、ジョブの先頭近くに // JCLLIB ORDER=(格納先のPDS) を置いて探索先を加えます。
JCLやCOBOLといったメインフレーム資産がいまも使われる背景はCOBOL(コボル)はなぜ廃れたと言われるのかで扱っています。
JCLからJavaを起動する2つの方式:JZOS Batch LauncherとBPXBATCH
ここで比較するのは、JVMをMVSのバッチプログラムとして直接起動するJZOS Batch Launcherと、z/OS UNIX(USS)のシェルを起動してその中で java コマンドを打つBPXBATCHの2つです。違いは次のとおりです。
| 観点 | JZOS Batch Launcher | BPXBATCH |
|---|---|---|
| 起動するもの | JVMを同じアドレス空間で直接起動 | USSのシェルかプログラム |
| Javaの終了コード | そのままステップのRC | SHRC指定で0〜255をそのまま |
| STDENVのシェル | 非ログインシェル | SH指定はログインシェル |
| DD文のデータセット | Javaから //DD:名前 で読める | USSファイルでの受け渡しが前提 |
| 入出力 | STDOUT・STDERRをDDのSYSOUTへ | 入力はUSSファイル、出力はUSS・SYSOUT・MVSデータセット |
| 向く用途 | 本番の定常ジョブ | 試験実行・既存シェルの流用 |
判断は単純で、ジョブスケジューラーの下でRCを見て後続を制御するジョブ、MVSのデータセットを読み書きするジョブはJZOSにします。BPXBATCHが向くのは、USSで動いているシェルスクリプトをとりあえずJCLから呼びたい場合や、java -version で環境を確かめる場合です。
BPXBATCHでjavaコマンドを実行するサンプルJCL
BPXBATCHは PARM='SH コマンド' でシェルを起動します。下の例では SH の代わりに SHRC を使い、標準出力と標準エラーはUSSのファイルに振っています。
//JAVAB JOB (ACCT),'BPXBATCH JAVA',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//STEP1 EXEC PGM=BPXBATCH,
// PARM='SHRC java -cp /u/app/classes com.example.MyBatch arg1'
//STDOUT DD PATH='/tmp/myapp.out',
// PATHOPTS=(OWRONLY,OCREAT,OTRUNC),PATHMODE=SIRWXU
//STDERR DD PATH='/tmp/myapp.err',
// PATHOPTS=(OWRONLY,OCREAT,OTRUNC),PATHMODE=SIRWXU
//STDENV DD *
JAVA_HOME=/usr/lpp/java/J21.0_64
PATH=/usr/lpp/java/J21.0_64/bin:/bin
/*
注意点は環境変数の優先順位です。IBMのz/OS 3.2のBPXBATCHの説明によると、SH指定ではログインシェルが動き、STDENVで渡した値は /etc/profile とユーザーの .profile の設定で上書きされます。STDENVに JAVA_HOME を書いたのに別の版のJavaが動く場合は、.profile 側の設定を疑ってください。
もう1つの注意点は戻り値です。BPXBATCHの説明によると、STDOUTやSTDERRをSYSOUTやMVSデータセットに割り当てた場合、_BPX_BATCH_SPAWN=YES の場合、TSOから実行した場合は、内部の戻り値がシェルの終了状態の256倍になります。ステップのRCはそれを4096で割った余りなので、終了状態1はRC=256、255はRC=3840と表示されます。SHRC(プログラムを直接起動するなら PGMRC)を指定すると、終了状態が0〜255のまま返ります。また、EXEC文のPARMは100文字までです。長いコマンドは STDPARM DDに書けば最大65,536バイトまで渡せます。
JZOS Batch Launcherの導入:JVMLDM21・JVMPRC21・JVMJCL21の配置
JZOSはSemeru Runtime Certified Edition for z/OS(z/OS向けのIBM製JDK)に同梱されていて、別途購入するものではありません。JZOSのInstallation and User’s Guide(Version 21)によると、部品はPDSEに置くロードモジュール、PROCLIBに置くサンプルPROC、SAMPLIBに置くサンプルJCLの3つです。版ごとの名前は次のとおりです。
| Java製品 | ロードモジュール | サンプルPROC | サンプルJCL |
|---|---|---|---|
| Semeru 25 | JVMLDM25 | JVMPRC25 | JVMJCL25 |
| Semeru 21 | JVMLDM21 | JVMPRC21 | JVMJCL21 |
| Semeru 17 | JVMLDM17 | JVMPRC17 | JVMJCL17 |
| Semeru 11 | JVMLDM16 | JVMPRC16 | JVMJCL16 |
| Java 8 64ビット | JVMLDM86 | JVMPRC86 | JVMJCL86 |
| Java 8 31ビット | JVMLDM80 | JVMPRC80 | JVMJCL80 |
Semeru 11だけは版番号と名前が一致しません(Version 11のガイドの表は JVMLDM16)。Semeru 25の名前はIBM DocsのVersion 25の導入手順、Java 8の名前はVersion 8のガイドの表によります。既存のジョブで JVMLDM86 や JVMPRC86 を見かけたら、64ビット版Java 8で動いているジョブです。
SMP/Eで導入した場合、既定ではロードモジュールが SYS1.SIEALNKE、PROCが SYS1.PROCLIB、JCLが SYS1.SAMPLIB に入ります。pax版を自分で展開した場合は、USSのシェルから次のようにコピーします。
cd $JAVA_HOME/mvstools
cp jcl/JVMJCL21 "//'SYS1.SAMPLIB(JVMJCL21)'"
cp jcl/JVMPRC21 "//'SYS1.PROCLIB(JVMPRC21)'"
cp -X JVMLDM21 "//'SYS1.SIEALNKE(JVMLDM21)'"
ロードモジュールを置いたライブラリがLNKLSTに入っていなければ、PROCの STEPLIB のコメントを外してそのPDSEを指します。サンプルJCLを投入して、SYSOUTに JVMJZBL1021N JZOS batch launcher completed, return code=0 と Hello World が出れば導入は完了です。
JZOSのサンプルJCLでJavaクラスを実行する
IBMのサンプル JVMJCL21 を元に、自分のクラスを動かす最小のジョブは次の形になります。JAVACLS に完全修飾のクラス名を、ARGS に引数を渡します。
//JAVAJOB JOB (ACCT),'JZOS BATCH',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//JAVA EXEC PROC=JVMPRC21,
// JAVACLS='com.example.DailyBatch',
// ARGS='2026-10-29'
//STDENV DD *
. /etc/profile
export JAVA_HOME=/usr/lpp/java/J21.0_64
export PATH=/bin:"${JAVA_HOME}"/bin
LIBPATH=/lib:/usr/lib:"${JAVA_HOME}"/bin
LIBPATH="$LIBPATH":"${JAVA_HOME}"/lib
LIBPATH="$LIBPATH":"${JAVA_HOME}"/lib/j9vm
export LIBPATH="$LIBPATH":
APP_HOME=/u/app
CLASSPATH=$APP_HOME/classes
for i in "${APP_HOME}"/lib/*.jar; do
CLASSPATH="$CLASSPATH":"$i"
done
export CLASSPATH="$CLASSPATH":
IJO="-Xms256m -Xmx512m"
export IBM_JAVA_OPTIONS="$IJO "
/*
//INPUT DD DSN=PROD.DAILY.INPUT,DISP=SHR
PROCの中身は EXEC PGM=JVMLDM&VERSION(既定の VERSION は21)で、REGION の既定値は 0M です。z/OS 3.2では、ほかの制御で上書きされない限り REGION=0M の指定で MEMLIMIT がNOLIMITになります。64ビットのJVMは -Xmx のヒープのほかに、最低512MBのネイティブヒープやスレッドごとのスタックも使うため、サイトの既定で MEMLIMIT を絞っている場合は不足に注意します。SYSOUT(JZOSのメッセージ)、STDOUT(System.out)、STDERR(System.err)はPROC側で SYSOUT=* に割り当て済みなので、呼び出し側のJCLは STDENV と、Javaが読むデータセットのDDを書けば足ります。
STDENVでJAVA_HOME・LIBPATH・CLASSPATHを設定する
STDENVは /bin/sh に渡されるシェルスクリプトで、JZOSでは必須のDDです。ガイドが挙げる決まりは4つあります。
- 設定したい変数は
exportする(exportしない変数はJVMに渡りません) - 行番号を入れない(ISPFで編集するときは NUMBER OFF にする)
- スクリプトの中で
exitを実行しない - ログインシェルではないので、
/etc/profileと.profileは自動では読まれない(必要なら. /etc/profileで読み込む)
BPXBATCHと逆の動きになる点に注意してください。また、STDENVで STEPLIB を export しても効きません。独自のロードライブラリを使うときは、JCLに JOBLIB か STEPLIB のDD文を書きます。LIBPATH には /lib・/usr/lib・$JAVA_HOME/bin などを含めます。上の例はIBMのサンプルの並びをそのまま使っています。
引数とJVMオプションの渡し方
クラス名と引数は、PARM(JAVACLS と ARGS)、環境変数 JZOS_MAIN_ARGS、DD名 MAINARGS のファイルの3か所から渡せて、この順に連結されます。PARMは文字数に限りがあるので、引数が長いときは //MAINARGS DD * に書きます。クラス名の代わりに -jar アプリ.jar と書けば、MANIFESTのMain-Classで起動します。ヒープなどのJVMオプションは IBM_JAVA_OPTIONS に入れ、そこで効かないオプションは JZOS_JVM_OPTIONS を使います。
Javaの終了コードとJCLのリターンコードの対応
JZOSは、Javaが System.exit(rc) で終わればその値を、main() が普通に戻れば0を、ステップのRCとして返します。RCとして使われるのは終了コードの下位12ビット(0〜4095)です。JZOS自身の異常には次の値を使います。
| RC | 名前 | 意味 |
|---|---|---|
| 0 | RC_OK | main()が正常終了、またはSystem.exit(0) |
| 100 | RC_MAIN_EXCEPTION | mainクラスが無い、またはmainが例外 |
| 101 | RC_CONFIG_ERR | 設定・準備の誤り |
| 102 | RC_SYSTEM_ERR | システム・内部エラー |
アプリ側で100〜102を使うと、JZOSの異常と区別がつかなくなります。業務上の警告は4、エラーは8や12のように、メインフレームの慣習に合わせた値を使うとJCL側も読みやすくなります。次の例は、Javaの終了コードが0のときだけ後続のステップを動かし、それ以外は通知用のステップを動かします。PROCの中のステップ名は JAVAJVM なので、RCは JAVA.JAVAJVM.RC で参照します。
//CHKRC IF (JAVA.JAVAJVM.RC = 0) THEN
//NEXT EXEC PGM=MYNEXT
// ELSE
//ALERT EXEC PGM=MYALERT
// ENDIF
RCではなく異常終了(ABEND)でジョブを止めたい運用なら、環境変数 JZOS_ABEND_EXIT=n(0〜99)を使います。終了コードが負かnより大きいと、JZOSはユーザーABEND U3333 で終わります。ガイドは、STDENVの処理に失敗したときにも効くよう、この変数を //CEEOPTS DD * に ENVAR("JZOS_ABEND_EXIT=99") と書く方法を勧めています。なお、非デーモンのスレッドが残っているとJZOSはその終了を待ちます。待たずに終わらせるには JZOS_GENERATE_SYSTEM_EXIT=true を設定します。
DD文で割り当てたデータセットをJavaで読む:JZOS Toolkit
標準の java.io が扱えるのはzFSなどのUNIXファイルだけです。PDS・PDSE・順次データセット・VSAM(KSDS・RRDS・ESDS)といったMVSのデータセットは、JZOS Toolkit(com.ibm.jzos パッケージ)で読み書きします。JZOSで起動したJavaはジョブステップと同じアドレス空間にいるので、JCLのDD名をそのまま指定できます。次のコードは、上のJCLの //INPUT DDを1行ずつ読んで件数を数え、0件なら4を返します。
package com.example;
import com.ibm.jzos.FileFactory;
import com.ibm.jzos.ZUtil;
import java.io.BufferedReader;
public class DailyBatch {
public static void main(String[] args) throws Exception {
int count = 0;
try (BufferedReader rdr = FileFactory.newBufferedReader("//DD:INPUT")) {
while (rdr.readLine() != null) {
count++;
}
}
System.out.println(ZUtil.getCurrentJobname() + " records=" + count);
System.exit(count == 0 ? 4 : 0);
}
}
このコードは、IBMが配布する ibm.jzos.jar(21.0.8.0)をクラスパスに入れて javac --release 21 でコンパイルが通ることを確認しています。Toolkitの多くの機能はJNIでz/OSのネイティブ機能を呼ぶため、本番の実行はz/OS上で行います。FileFactory は名前が // で始まればMVSデータセット、そうでなければUNIXファイルとして開くので、FileFactoryによる読み取り部分は、PCではファイルパス、本番ではDD名に切り替えて共用できます。サンプル全体をPCで試す場合は、z/OS専用の ZUtil.getCurrentJobname() 呼び出しも分離する必要があります。
大量のレコードを順に処理するなら RecordReader と RecordWriter を使います。BSAMで読み書きするクラスで、ガイドには ZFile と比べてCPU使用量が60〜70%減った例もあるとしています。VSAMの読み書きや位置決めが必要な処理は ZFile を使います。IBMはToolkitの使用例をGitHubのzsystems/java-samplesで公開していて、VSAM KSDSの操作やDFSORTの呼び出し、ジョブの投入の例があります。
USSでのコンパイルと文字コードの取り違えを防ぐ設定
ソースはUSSのディレクトリに置き、z/OS上の javac でコンパイルするか、PCでビルドしたjarをバイナリ転送します。本記事で使うSemeru 21の既定の file.encoding はUTF-8です。Semeru 17以前では、ロケールに応じたEBCDIC系の文字コードが既定でした。UTF-8で書いたソースをz/OSでコンパイルするときは文字コードを明示します。
export JAVA_HOME=/usr/lpp/java/J21.0_64
$JAVA_HOME/bin/java -version
$JAVA_HOME/bin/javac -encoding UTF-8 -d /u/app/classes /u/app/src/com/example/DailyBatch.java
文字化けは3か所で起きます。1つめはソースのコンパイル時で、-encoding で防ぎます。2つめはSTDOUT・STDERRへの出力で、JZOSは System.out を JZOS_OUTPUT_ENCODING(未指定ならロケールの既定、多くはIBM-1047)に変換して書きます。3つめはMVSデータセットの読み書きで、ガイドは ZUtil.getDefaultPlatformEncoding() でエンコーディングを指定するよう勧めています。XMLなどを生成する場合は、入出力APIで必要な文字コードを明示します。-Dfile.encoding=COMPAT は旧版のネイティブ文字コード選択に戻す設定であり、ASCIIを指定するものではありません。
Semeru 21では、JZOSで起動したJavaのLogger出力がジョブログで文字化けする不具合がAPAR PH68459として報告され、21.0.9.0で修正されています。21.0.6と21.0.7の回避策は、STDENVで export JZOS_ENABLE_OUTPUT_TRANSCODING=false を設定することです。
z/OSで使うJavaの版とサポート期限(2026年10月時点)
z/OS向けのJavaは、現在はIBM Semeru Runtime Certified Edition for z/OS(OpenJDKとEclipse OpenJ9をベースにしたIBMのJDK)が主力です。IBMのJava SDK Products on z/OSには、V11・V17・V21・V25が載っています。各版の期限はIBMのライフサイクル表で次のとおりです。
| 製品 | 標準サポート終了 | 延長サポート終了 |
|---|---|---|
| Java 8(64ビット) | 2026年9月30日 | 2030年12月31日 |
| Semeru 11 | 2025年11月30日 | 2027年11月30日 |
| Semeru 17 | 2026年9月30日 | 2028年9月30日 |
| Semeru 21 | 2027年12月31日 | 2029年12月31日 |
| Semeru 25 | 未記載 | 未記載 |
Semeru 25の一般提供は2026年4月10日、z/OS 3.2の一般提供は2025年9月30日です。IBMが2026年10月6日に更新したz/OSの要素とJavaの依存関係では、z/OS 3.1と3.2のOS提供機能はSemeru 25の利用を前提にしています。一方で利用者のアプリケーションは、Java 21・17・11・8のそれぞれがサポート期間内であれば使い続けられます。
結論として、Java 8とSemeru 17・11で動いているジョブは、すでに標準サポートの外にあります。新しく組むジョブはSemeru 21か25で作ります。Semeru 21で組む場合も、標準サポートが2027年末に切れる前にSemeru 25(JVMPRC25)へ上げる計画を立ててください。版の変更によってGCの挙動や必要なヒープ容量が変わる可能性があるため、IBM_JAVA_OPTIONS は移行時に見直します。
COBOLやPL/Iの資産をJavaに置き換える場合は、ジョブ単位で書き換えるかシステムごと作り直すかで工程が大きく変わります。方式の比較はモダナイゼーションとは?レガシー刷新の手法と実装アプローチ・進め方にまとめています。移行の計画から実装まで外部に相談したい場合は、アイソーのシステムマイグレーション・リプレイスで対応しています。
ジョブが失敗したときの切り分け:SDSFのログとLOGLVL
ジョブの結果はSDSFで確認します。JESMSGLGでステップごとのRCを、SYSOUTでJZOSのメッセージ(JVMJZBL で始まる)を、STDOUTとSTDERRでJavaの出力とスタックトレースを見ます。よくある症状と対処は次のとおりです。
| 症状 | 主な原因 | 対処 |
|---|---|---|
| RC=100 | クラスが無い・mainが例外 | CLASSPATHとSTDERRを確認 |
| RC=101 | STDENVなど設定の誤り | SYSOUTのJVMJZBLメッセージを確認 |
| JVMが起動しない | JAVA_HOMEやLIBPATHの誤り | 同じユーザーIDでjava -version |
| ジョブログが文字化け | 出力の文字コード | JZOS_OUTPUT_ENCODINGを確認 |
| 終わらない | 非デーモンのスレッドが残る | JZOS_GENERATE_SYSTEM_EXIT |
| メモリ不足で落ちる | MEMLIMITやREGIONが小さい | REGION=0MかMEMLIMITを指定 |
原因が見えないときは、ログの詳しさを上げます。PROCのパラメーター LOGLVL に '+I' を指定すると、JVMを起動する前の環境変数(CLASSPATHを含む)がSYSOUTに出ます。'+D' ではSTDENVスクリプトの入出力が出るので、スクリプトの先頭に set -x を書けば1行ずつの実行を追えます。'+T' はIBMに問題を報告するときのトレースです。どのクラスが見つからないのかを知りたいときは、IBM_JAVA_OPTIONS に -verbose:class を足します。
//JAVA EXEC PROC=JVMPRC21,LOGLVL='+I',
// JAVACLS='com.example.DailyBatch'
それでも起動しない場合は、ジョブのユーザーIDがUSSを使えるように設定されているかを確認します。ガイドの手順は、そのユーザーIDでUSSにログインし、$JAVA_HOME/bin/java -version が通るかを試すことです。
よくある質問
z/OS JCLとは何ですか?
z/OSにバッチジョブの内容を伝える制御文です。JOB文(ジョブの宣言)、EXEC文(実行するプログラムやプロシージャ)、DD文(データセットの割り当て)の3種類で書き、各行は // で始めます。
JCLからJavaプログラムを実行するにはどうすればよいですか?
JZOS Batch LauncherのPROC(Semeru 21なら JVMPRC21)を EXEC PROC= で呼び、JAVACLS にクラス名を、STDENV DDに JAVA_HOME と CLASSPATH を書きます。試験的に動かすだけなら、PGM=BPXBATCH でシェルから java コマンドを実行する方法もあります。
JZOS Batch Launcherとは何ですか?
JVMをMVSのバッチプログラムやスタートタスクとして起動する、IBMのz/OS向けランチャーです。Semeru Runtime Certified Edition for z/OSに同梱されていて、Javaの終了コードがステップのRCになります。
BPXBATCHとJZOSはどちらを使うべきですか?
RCで後続を制御する定常ジョブとMVSのデータセットを扱うジョブはJZOS、既存のUSSシェルスクリプトの呼び出しや試験実行はBPXBATCHです。
JVMLDM86とは何ですか?
64ビット版のIBM SDK for z/OS Java Technology Edition Version 8に同梱されるJZOSのロードモジュールです(PROCは JVMPRC86)。Java 8の標準サポートは2026年9月30日に終わっているので、Semeru 21の JVMLDM21・JVMPRC21 への切り替えを検討してください。