Java

z/OS JCLでJavaバッチを実行する手順:JZOS Batch LauncherとBPXBATCHのサンプルJCL

z/OS JCLでJavaバッチを実行する手順:JZOS Batch LauncherとBPXBATCHのサンプルJCL

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・サンプルJCL JVMJCL21)で動きます。数字はSemeruの版に対応しますが、Semeru 11だけは JVMLDM16 という名前です。
  • 環境変数は STDENV DDのシェルスクリプトで 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 への切り替えを検討してください。

関連記事

お気に入りに入れた記事の一覧

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.27 テックブログ Brotli 脆弱性:CVE-2026-111497の影響確認とNode.js・Python・.NETの更新手順
  2. 2026.10.28 テックブログ リンツ不正アクセス:314万件の対象確認とパスワード強制リセット後の対処
  3. 2026.10.27 テックブログ Joshin(ジョーシン)不正アクセス:最大600万件の対象とサブスク初期パスワードの対処
  4. 2026.10.23 テックブログ Renta!はいつ再開?不正アクセスでメールアドレス等が流出したおそれ・再開時のパスワード再設定と運営側の対応
  5. 2026.10.28 テックブログ 楽天トラベル不正アクセス:最大約260万件の対象確認とカード下4桁の詐欺対策

RELATED POSTS 関連記事

目次