1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JCLを読めると何がわかるのか──ジョブ制御言語から見えるバッチ処理の本質

1
Posted at

はじめに

「JCLって何ですか」と聞かれて、「ジョブ制御言語です」と答えるのは正しい。しかしそれだけでは何もわからない

JCLを初めて見た人の反応は、大抵こうだ。

//MYJOB   JOB (ACCT),'SAMPLE JOB',CLASS=A,MSGCLASS=X
//STEP1   EXEC PGM=IEFBR14
//INFILE  DD  DSN=MY.INPUT.FILE,DISP=SHR
//OUTFILE DD  DSN=MY.OUTPUT.FILE,DISP=(NEW,CATLG,DELETE),
//            UNIT=SYSDA,SPACE=(CYL,(5,2),RLSE)
//SYSOUT  DD  SYSOUT=*

//で始まる行は何?」「DSNって何?」「DISP=(NEW,CATLG,DELETE)の3つのカッコは?」

最初は暗号に見える。しかし読めるようになると、バッチ処理の設計思想が丸ごと見えてくる。JCLは単なる制御言語ではなく、メインフレームが60年かけて磨いた「大量データ処理のアーキテクチャパターン集」だ。

この記事では、JCLを読めるようになること、そしてそこから何が見えるようになるかを解説する。


1. JCLとは何か──3行で

JCLの役割を一言で言えば、「どのプログラムを・どのファイルを使って・どの順番で動かすか」を記述する指示書だ。

JCLの文(ステートメント)は3種類しかない。

ステートメント 役割 Linuxで言うと
JOB ジョブ全体の定義(課金情報・優先度・通知先) シェルスクリプトのヘッダー部分
EXEC 実行するプログラムまたは手続き(PROC)の指定 コマンドの呼び出し
DD データ定義(ファイルの論理名と物理名のマッピング) リダイレクト(<>)または環境変数

この3種類だけで、数十ステップにわたる複雑なバッチ処理を記述する。

Linuxのシェルスクリプトとの根本的な違い

Linuxのシェルスクリプトと比べると、JCLの設計思想の違いが際立つ。

# Linuxシェルスクリプト:手続き的、コードとデータが密結合
#!/bin/bash
INPUT=/data/input.csv
OUTPUT=/data/output.csv
java -jar processor.jar < $INPUT > $OUTPUT
if [ $? -ne 0 ]; then
    echo "失敗"
    exit 1
fi
//* JCL:宣言的、プログラムとデータが疎結合
//STEP1   EXEC PGM=PROCESSOR
//INFILE  DD  DSN=MY.INPUT.FILE,DISP=SHR
//OUTFILE DD  DSN=MY.OUTPUT.FILE,DISP=(NEW,CATLG,DELETE)
//SYSOUT  DD  SYSOUT=*

シェルスクリプトはプログラムがファイルパスを直接知っている(密結合)。JCLではプログラムが知るのは論理名(INFILEOUTFILE)だけで、物理的なファイルの場所はJCLが担う(疎結合)。この設計思想は、現代で言えばDI(依存性の注入)に相当する。


2. JCLを読む──基本構造の解読

実際のJCLを行ごとに読み解く。以下は「入力ファイルを読み込んで処理し、出力ファイルを生成する」という典型的なバッチジョブだ。

//PAYJOB   JOB (0001),'PAYROLL BATCH',              (1)
//             CLASS=B,MSGCLASS=X,NOTIFY=&SYSUID
//***********************************************
//* 給与計算バッチ処理
//***********************************************
//STEP010  EXEC PGM=PAYROLL1                        (2)
//TRANSIN  DD  DSN=PAYROLL.TRANS.INPUT,             (3)
//             DISP=SHR
//MASTERIO DD  DSN=PAYROLL.MASTER.FILE,             (4)
//             DISP=OLD
//RPTOUT   DD  SYSOUT=A                             (5)
//SYSOUT   DD  SYSOUT=*
//*
//STEP020  EXEC PGM=PAYROLL2,                       (6)
//             COND=(0,NE,STEP010)
//CALCIN   DD  DSN=PAYROLL.MASTER.FILE,             (7)
//             DISP=SHR
//TAXOUT   DD  DSN=PAYROLL.TAX.OUTPUT,
//             DISP=(NEW,CATLG,DELETE),             (8)
//             UNIT=SYSDA,
//             SPACE=(TRK,(100,50),RLSE)
//SYSOUT   DD  SYSOUT=*

注:カラム制限について
実際のJCLでは、継続行は「2〜3列目が//、続くパラメータは16列目以降に記述」「前の行末はカンマで終わる」という厳密なカラム制限がある。本記事では可読性のためインデントを整えているが、実機では桁数を守らないとJCLエラーになる点に注意してほしい。

各行の読み方

(1) JOB文:ジョブの宣言

//PAYJOB   JOB (0001),'PAYROLL BATCH',CLASS=B,MSGCLASS=X,NOTIFY=&SYSUID
  • //PAYJOB ── ジョブ名(8文字以内)
  • JOB ── このステートメントがJOB文であることの宣言
  • (0001) ── 課金アカウント番号
  • 'PAYROLL BATCH' ── ジョブの説明(SYSLOG・コンソールに表示される)
  • CLASS=B ── ジョブクラス(優先度や実行キューの指定)
  • MSGCLASS=X ── ジョブログの出力先
  • NOTIFY=&SYSUID ── 完了通知を送るユーザー(&SYSUIDは実行ユーザーの変数)

(2) EXEC文:プログラムの実行

//STEP010  EXEC PGM=PAYROLL1
  • //STEP010 ── ステップ名(このジョブ内でユニークな名前)
  • EXEC PGM=PAYROLL1 ── PAYROLL1というロードモジュールを実行する

EXECにはPGM=(プログラム直接指定)とPROC=(手続き呼び出し)の2種類がある。PROCはJCLのサブルーチンに相当し、共通処理を再利用できる。

(3)(4) DD文:データ定義(入力ファイル)

//TRANSIN  DD  DSN=PAYROLL.TRANS.INPUT,DISP=SHR
//MASTERIO DD  DSN=PAYROLL.MASTER.FILE,DISP=OLD
  • //TRANSIN ── 論理ファイル名(プログラムはこの名前でファイルを参照する)
  • DD ── Data Definitionの略
  • DSN= ── Data Set Name(物理ファイル名)
  • DISP=SHR ── 共有アクセス(他ジョブも同時に読める)
  • DISP=OLD ── 排他アクセス(このジョブだけが使用、他からはアクセス不可)

(5) SYSOUT:帳票・ログ出力

//RPTOUT   DD  SYSOUT=A
//SYSOUT   DD  SYSOUT=*
  • SYSOUT=A ── 帳票クラスAのプリンタキューへ出力
  • SYSOUT=* ── ジョブログと同じ出力クラスへ(コンソール/ログ)

(6) EXEC文(条件付き):前ステップの結果を見て実行を制御

//STEP020  EXEC PGM=PAYROLL2,COND=(0,NE,STEP010)
  • COND=(0,NE,STEP010) ── 「0 ≠ STEP010のRC」が真のとき、このステップをスキップする

つまり「STEP010が正常終了(RC=0)したときだけSTEP020を実行する」という条件制御だ。CONDパラメータについては次節で詳しく解説する。

(7)(8) DD文(出力ファイル):ファイルのライフサイクル定義

//TAXOUT   DD  DSN=PAYROLL.TAX.OUTPUT,
//             DISP=(NEW,CATLG,DELETE),
//             UNIT=SYSDA,
//             SPACE=(TRK,(100,50),RLSE)
  • DISP=(NEW,CATLG,DELETE) ── 3つの状態指定(後述)
  • UNIT=SYSDA ── ディスク装置タイプの指定
  • SPACE=(TRK,(100,50),RLSE) ── トラック単位で100トラック確保、追加は50トラック、未使用は解放

3. JCLが見せる「バッチ処理の設計思想」

CONDパラメータ──ステップ間の条件制御

JCLのCONDパラメータは、前のステップの結果を見て後続ステップの実行を制御する仕組みだ。

//STEP010  EXEC PGM=VALIDATE
//STEP020  EXEC PGM=TRANSFORM,
//             COND=(0,NE,STEP010)
//STEP030  EXEC PGM=LOAD,
//             COND=((0,NE,STEP010),(0,NE,STEP020))
//STEP040  EXEC PGM=NOTIFY,
//             COND=(0,LT)

CONDの評価式の構造は COND=(指定値, 演算子, ステップ名) で、評価は「指定値 [演算子] 実際のRC」という順序になる。

COND=(4,LT,STEP010) の評価:
  「4 < STEP010のRC」が真 → スキップ
  つまり「RCが4より大きければスキップ」

CONDの演算子と意味:

演算子 評価式 スキップする条件
EQ 指定値 == RC 指定値とRCが等しいとき
NE 指定値 != RC 指定値とRCが等しくないとき
LT 指定値 < RC RCが指定値より大きいとき
GT 指定値 > RC RCが指定値より小さいとき
LE 指定値 <= RC RCが指定値以上のとき
GE 指定値 >= RC RCが指定値以下のとき

CONDパラメータには2つの直感に反するポイントがある。

① 条件が真のとき「実行する」ではなく「スキップする」

COND=(0,NE,STEP010)は「0 ≠ STEP010のRC」が真(=RCが0以外)のときにスキップ。裏を返せば「STEP010がRC=0(正常)のときだけ実行する」となる。

② 評価の主語は「指定値」

COND=(4,LT,STEP010)は「4 < RCのときスキップ」だ。「RCが4より小さいときスキップ」ではない。指定値が左辺になることを意識しないと、LT・GTの読み方が逆になる。

この2点を頭に入れておくと、どんなCOND指定も迷わず読める。

DISPパラメータ──ファイルのライフサイクル管理

DISPパラメータの3つの値は、ファイルのライフサイクル全体を定義する。

DISP=(開始時の状態, 正常終了時の処置, 異常終了時の処置)
開始時の状態 意味
NEW 新規作成
OLD 既存・排他アクセス
SHR 既存・共有アクセス
MOD 既存に追記、なければ新規作成
終了時の処置 意味
CATLG カタログに登録(次回も名前でアクセス可能)
UNCATLG カタログから削除(ファイル自体は残る)
DELETE ファイルを削除
KEEP そのまま保持(カタログ操作なし)
PASS 次のステップへ引き渡す(ジョブ内でのみ有効)

典型的なパターンの読み方:

DISP=(NEW,CATLG,DELETE) ── 新規作成。正常終了でカタログ登録(次回アクセス可)、異常終了で削除(不完全なゴミを残さない)

DISP=(OLD,KEEP,KEEP) ── 排他アクセス。正常でも異常でもファイルはそのまま保持

DISP=(SHR,PASS,DELETE) ── 共有で読んで次のステップへ引き渡す。異常終了なら削除

DISP=(MOD,CATLG,CATLG) ── 追記。正常でも異常でもカタログに残す

DISP=(NEW,CATLG,DELETE)というパターンを見たとき、それは「このファイルは中間生成物で、正常終了時は次のジョブで使うために登録しておき、異常終了時は不完全なファイルをゴミとして残さない」という設計意図を示している。コードの1行から、設計者のリカバリー戦略が読み取れる。

DDNAMEによる論理名と物理名の分離

JCLの最も重要な設計原則が、プログラムはファイルの論理名しか知らないという疎結合だ。

プログラム内のコード:
  READ FROM INFILE    ← 論理名だけ知っている

JCL側の定義:
  //INFILE DD DSN=PROD.INPUT.DATA.2026,DISP=SHR
                      ↑ 物理的なファイル名はここで定義

環境(本番/テスト/DR)が変わるときも、プログラムを変更する必要はない。JCLのDSN指定を変えるだけでよい。

Javaで言えばこれは依存性の注入(DI)そのものだ:

// Javaの場合(SpringのDI)
@Autowired
private DataSource dataSource;  // ← 論理名だけ知っている
// 実際のDBは設定ファイル(application.properties)で定義
//* JCLの場合
//INFILE  DD  DSN=PROD.INPUT.DATA,DISP=SHR
//*                ↑ 環境設定(application.propertiesに相当)

JCLはSpringが登場する何十年も前から、この分離を実践していた。


4. JCLを読むと「移行の難しさ」がわかる

ステップ依存関係はDAGとして読める

複数ステップのJCLを図にすると、**有向非巡回グラフ(DAG)**として表現できる。

STEP010(バリデーション)
    │ RC=0のみ通過
    ▼
STEP020(変換処理A)    STEP030(変換処理B)
    │ RC=0のみ通過          │ RC=0のみ通過
    └──────────┬────────────┘
               ▼
           STEP040(マージ・ロード)
               │
               ▼
           STEP050(通知・クリーンアップ)
               ※ RC>0でも実行(COND省略)

このDAGをモダンなワークフローエンジン(Apache Airflow、AWS Step Functions)に移行するとき、CONDパラメータの条件を正確に再現する必要がある。「評価の主語は指定値」「真のときスキップ」という2つのポイントを誤解すると、異常時の分岐が逆転したまま動いてしまう。

RESTARTパラメータが示すチェックポイント設計

JCLのJOB文にはRESTARTパラメータがある。

//PAYJOB   JOB (0001),'PAYROLL BATCH',RESTART=STEP030

これは「STEP030から再実行する」という指定だ。10ステップのジョブが7ステップ目で失敗した場合、最初からやり直すのではなく、任意のステップから再開できる。

この仕組みは、現代のCI/CDパイプラインの「失敗したジョブだけ再実行」に相当するが、JCLは1960年代からこれを実装していた。

移行設計でここを見落とすと、失敗時の再実行手順が設計されないまま本番稼働することになる。

PASSとTEMPファイル──ステップ間のデータ受け渡し

ステップ間でデータを受け渡すパターンとして、PASSと一時ファイル(TEMP)がある。

//STEP010  EXEC PGM=EXTRACT
//TEMPDATA DD  DSN=&&TEMP,
//             DISP=(NEW,PASS,DELETE),
//             UNIT=SYSDA,SPACE=(CYL,(10,5))

&&TEMP&&で始まる名前が一時ファイル)はSTEP010で生成され、DISP=(NEW,PASS,DELETE)によって次のステップへ引き渡される。異常終了時は削除される。

//STEP020  EXEC PGM=TRANSFORM
//TEMPDATA DD  DSN=&&TEMP,
//             DISP=(OLD,DELETE,DELETE)

STEP020が&&TEMPを受け取り、使い終わったら正常/異常を問わず削除する。

この一時ファイルはジョブの外からはアクセスできない、ステップ間の「パイプ」として機能している。

Linuxのパイプ(|)との違いは、JCLの一時ファイルがディスクを経由することだ。前のステップが完全に書き終わってから次のステップが読み始める。これが「シーケンシャルなバッチ処理」の本質であり、大量データをメモリに持たずに処理できる理由でもある。


5. JCLをモダンなパイプラインと対比する

JCLの概念は、現代のCI/CD・ワークフローエンジンと構造的に対応している。

JCL GitHub Actions Apache Airflow 説明
JOB workflow DAG 処理全体の定義
STEP job / step Task 処理の単位
COND=(0,NE) if: steps.X.outcome == 'success' trigger_rule='all_success' 前段の結果による実行制御
DD DSN= env: FILE_PATH= op_kwargs={'path': ...} 外部リソースの注入
DISP=(NEW,CATLG,DELETE) アーティファクトのアップロード/クリーンアップ XCom / S3 中間成果物の管理
RESTART=STEP030 jobs.<id>.needs + 再実行 depends_on_past チェックポイントからの再開
SYSOUT=* runs-on のログ出力 log 実行ログの管理
PROCの呼び出し Reusable Workflow SubDAG / TaskGroup 共通処理の再利用

GitHub Actionsで書き直すと

先ほどのJCLサンプルをGitHub Actionsのイメージに翻訳すると:

# GitHub Actions(JCLの給与計算バッチに相当するイメージ)
name: Payroll Batch
on:
  schedule:
    - cron: '0 1 * * *'  # 毎日午前1時

jobs:
  validate:                          # STEP010: EXEC PGM=PAYROLL1
    runs-on: ubuntu-latest
    steps:
      - name: Run validation
        run: java -jar payroll1.jar
        env:
          TRANSIN: s3://payroll/trans/input     # DD TRANSIN
          MASTERIO: s3://payroll/master/file    # DD MASTERIO

  transform:                         # STEP020: EXEC PGM=PAYROLL2
    needs: validate                  # COND=(0,NE,STEP010) に相当
    if: success()                    # 正常終了のみ実行
    runs-on: ubuntu-latest
    steps:
      - name: Run transformation
        run: java -jar payroll2.jar

JCLとGitHub Actionsは記法がまったく異なるが、表現している設計パターンは同一だ。

この対比がわかると、JCLの移行先としてAirflowやStep Functionsを選んだとき、どのパラメータを何に対応させるかが自然に見えてくる。


JCLが読めると何がわかるのか──まとめ

JCLを読めるようになると、以下のことが見えてくる。

技術的に見えること

  • バッチジョブの依存関係がDAGとして可視化できる
  • 各ステップのリカバリー設計(COND・RESTART・DISP)が読み取れる
  • ファイルのライフサイクル(作成・保持・削除)が明示されている

設計思想として見えること

  • プログラムとデータの疎結合(論理名と物理名の分離)
  • 中間データの管理戦略(PASS・一時ファイル・カタログ)
  • 異常時の振る舞いが正常時と同じ粒度で設計されている

移行設計に役立つこと

  • CONDパラメータの条件をAirflow/Step Functionsに正確にマッピングできる
  • DISPパラメータからS3ライフサイクルポリシーの設計ができる
  • RESTARTパラメータからチェックポイント設計を再構築できる

JCLは「古い記法の制御ファイル」ではない。60年間、止まらず動き続けてきたバッチ処理のベストプラクティスが、宣言的な形式で結晶化したものだ。


おわりに

JCLを読めるようになることは、メインフレームへの「入門」ではない。大量データ処理の設計思想を、その源流から理解することだ。

今日のCI/CDパイプライン、ワークフローエンジン、データパイプラインが解決しようとしている問題──依存関係の制御、チェックポイントからの再開、中間データの管理──は、JCLがすでに1960年代に解いていた問題と同じだ。

読めなかったJCLが読めるようになったとき、現代のアーキテクチャが「新しいもの」ではなく「同じ問題への別の答え」として見えてくる。


本記事ではJCLの読み方と設計思想の概要を扱ったが、**「富士通・日立・IBM各社の撤退タイムラインと現場への影響」「2030/2035年問題のキャパシティ・クランチ」**など、日本のメインフレームの現状と課題については、noteの連載で詳しく論じている。

第0章 日本の汎用機市場の現在地と2035年問題(note)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?