kagamihogeの日記

kagamihogeの日記です。

Spring Batch 4.1.x - Reference Documentation - JSR-352 Supportのテキトー翻訳

https://docs.spring.io/spring-batch/4.1.x/reference/html/jsr-352.html#jsr-352

https://qiita.com/kagamihoge/items/12fbbc2eac5b8a5ac1e0 俺の訳一覧リスト

*1

1. JSR-352 Support

Spring Batch 3.0以降はJSR-352を完全に実装しています。このセクションは仕様自体の入れ替えというより、JSR-352固有のコンセプトをSpring Batchに適用する方法について説明します。JSR-352の情報についてはJCP https://jcp.org/en/jsr/detail?id=352 を参照してください。

1.1. General Notes about Spring Batch and JSR-352

Spring BatchとJSR-352は構造的には同じです。どちらもjobはstepで作り、reader, processor, writer, listenerを持ちます。しかし、微妙に異なる箇所があります。たとえば、Spring Batchのorg.springframework.batch.core.SkipListener#onSkipInWrite(S item, Throwable t)は引数を2つ、スキップされたアイテムとスキップ原因の例外、を取ります。JSR-352の方(javax.batch.api.chunk.listener.SkipWriteListener#onSkipWriteItem(List<Object> items, Exception ex))も同様に引数を2つとります。しかし、最初の引数はそのchunkの全アイテムListで2つ目はスキップ原因のExceptionです。こうした差異があるため、Spring Batchでjobを実行する方法が2つ、従来通りのSpring BatchのjobかJSR-352ベースのjob、がある点には注意が必要です。Spring Batchのアーティファクト(reader, writerなど)のjobをJSR-352のJSLで設定してJsrJobOperatorで実行する場合、JSR-352のルールで動作します。なお、JSR-352で開発したバッチアーティファクトはSpring Batchのjobでは動作しない点に注意してください。

1.2. Setup

1.2.1. Application Contexts

Spring Batch内でのJSR-352ベースのjobは2つのアプリケーションコンテキストを構成します。親コンテキストは、JobRepository, PlatformTransactionManagerなどSpring Batchのインフラ部分に関連するbeanを持ち、子コンテキストは実行するjobの設定を持ちます。親コンテキストはフレームワークが提供するjsrBaseContext.xmlで定義します。このコンテキストはJSR-352-BASE-CONTEXTシステムプロパティでオーバーライドできます。

※ ベースコンテキスト(base context)はプロパティインジェクションなどのJSR-352プロセッサで処理しないため、追加処理の必要なコンポーネントはそこで設定しないでください*2

1.2.2. Launching a JSR-352 based job

JSR-352でバッチジョブを実行する方法は大変シンプルです。ジョブを実行するのに必要なコードは以下の通りです。

JobOperator operator = BatchRuntime.getJobOperator();
jobOperator.start("myJob", new Properties());

上記は開発者にとって分かりやすいですが、落とし穴があります*3。Spring Batchは裏側でいくつかのインフラとなるbeanを初期化します。これは開発者がオーバーライド可能です。以下は初回BatchRuntime.getJobOperator()呼び出し時に初期化される一覧です。

Bean Name Default Configuration Notes
dataSource 設定値使用のApache DBCP BasicDataSource デフォルトではHSQLDBが初期化される
transactionManager org.springframework.jdbc.datasource.DataSourceTransactionManager 上記で定義するdataSource beanを参照する
A Datasource initializer   batch.drop.scriptbatch.schema.scriptプロパティのスクリプトを実行するために設定します。デフォルトではHSQLDBスキーマスクリプトを実行します。この振る舞いはbatch.data.source.initプロパティでdisableに出来ます。
jobRepository JDBCベースのSimpleJobRepository JobRepositoryは上述のデータソースとトランザクションマネージャを使用します。スキーマのテーブルプレフィクス(デフォルトBATCH_)はbatch.table.prefixプロパティで設定可能です
jobLauncher org.springframework.batch.core.launch.support.SimpleJobLauncher job実行に使用
batchJobOperator org.springframework.batch.core.launch.support.SimpleJobOperator JsrJobOperatorが各種機能を提供するのにこのbeanをラップする
jobExplorer org.springframework.batch.core.explore.support.JobExplorerFactoryBean JsrJobOperatorが提供する機能のルックアップに使用する
jobParametersConverter org.springframework.batch.core.jsr.JsrJobParametersConverter JobParametersConverterのJSR-352固有実装
jobRegistry org.springframework.batch.core.configuration.support.MapJobRegistry SimpleJobOperatorが使用
placeholderProperties org.springframework.beans.factory.config.PropertyPlaceholderConfigure 上述のプロパティを設定するためにbatch-${ENVIRONMENT:hsql}.propertiesをロードする。ENVIRONMENTはシステムプロパティ(デフォルトhsql)でSpring BatchがサポートするDBを指定可能です

※ 上記beanのいずれもJSR-352ベースのjob実行ではオプション扱いです。どのbeanも必要に応じてカスタマイズのためにオーバライドが可能です。

1.3. Dependency Injection

JSR-352はかなりの程度Spring Batchのプログラミングモデルをベースにしています。よって、明示的に何らかのDI実装を用意する必要はありません。Spring BatchはJSR-352で定義するバッチアーティファクトのロードに3つの方法をサポートしています。

  • Implementation Specific Loader - Spring BatchはSpring上で動作するので、JSR-352バッチジョブ内のSpring DIもサポートする。
  • Archive Loader - JSR-352は論理名とクラス名をマッピングするbatch.xmlを定義する。このファイルを使用する場合は/META-INF/ディレクトリ内に配置する。
  • Thread Context Class Loader - JSR-352はインライン完全修飾クラス名によるJSLでバッチアーティファクトの実装を指定する設定が可能です。Spring BatchはJSR-352設定のjobも同様にサポートします。

JSR-352ベースのバッチジョブでSpringのDIを使うには、Springアプリケーションコンテキストを使用するバッチアーティファクトをbeanとして設定します。bean定義後は、jobはそのbeanをbatch.xmlで定義されたかのように参照可能です。

Java Configuration

@Configuration
public class BatchConfiguration {

    @Bean
    public Batchlet fooBatchlet() {
        FooBatchlet batchlet = new FooBatchlet();
        batchlet.setProp("bar");
        return batchlet;
       }
}


<?xml version="1.0" encoding="UTF-8"?>
<job id="fooJob" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
    <step id="step1" >
        <batchlet ref="fooBatchlet" />
    </step>
</job>

Springコンテキストのassembly(importsなど)は、Springベースのアプリケーション同様に、JSR-352のjobでも動作しまう。JSR-352ベースのjobとの唯一の相違点は、コンテキスト定義のエントリーポイントは/META-INF/batch-jobs/のjob定義となります。

thread context class loaderの方法を使うには、refで完全修飾クラス名を渡します。この方法かbatch.xmlを使う場合、参照クラスはbean生成のために引数無しコンストラクタが必要です。

<?xml version="1.0" encoding="UTF-8"?>
<job id="fooJob" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
    <step id="step1" >
        <batchlet ref="io.spring.FooBatchlet" />
    </step>
</job>

1.4. Batch Properties

1.4.1. Property Support

JSR-352は、JSLの設定方法を使用して、Job,Step,バッチアーティファクトレベルで定義するプロパティを使用可能です。バッチプロパティは以下の方法で各レベルで設定します。

<properties>
    <property name="propertyName1" value="propertyValue1"/>
    <property name="propertyName2" value="propertyValue2"/>
</properties>

Propertiesは各バッチアーティファクトに対して設定します。

1.4.2. @BatchProperty annotation

バッチアーティファクトPropertiesはクラスのフィールドに@BatchProperty@Inject(仕様上両方のアノテーションが必要)を付与することで参照します。JSR-352の定義上、プロパティのフィールドはString型が必須です。型変換の実装と実行は開発者の責任です。

javax.batch.api.chunk.ItemReaderアーティファクトは上述のプロパティ定義と共に使用可能で以下のようにアクセスします。

public class MyItemReader extends AbstractItemReader {
    @Inject
    @BatchProperty
    private String propertyName1;

    ...
}

"propertyName1"フィールドの値は"propertyValue1"になります。

1.4.3. Property Substitution

デフォルトプロパティ(Property substitution)*4演算子とシンプルな条件付きの式を使います。一般的な使用法は#{operator['key']}です。

サポートする演算子は以下です。

  • jobParameters - job開始/再開後にjobパラメータ値にアクセス。
  • jobProperties - JSLのjobレベルのプロパティにアクセス。
  • systemProperties - 名前付きシステムプロパティにアクセス。
  • partitionPlan - パーティション化stepのパーティーションプランの名前付きプロパティにアクセス。
#{jobParameters['unresolving.prop']}?:#{systemProperties['file.separator']}

左側が使いたい値で、右側がデフォルト値です。この例では、#{jobParameters['unresolving.prop']}が解決不能であれば、システムプロパティfile.separatorの値となります。両方とも解決不能であれば、空文字列を返します。複数の条件を使用可能で、';'で区切ります。

1.5. Processing Models

JSR-352にはSpring Batchと同様の2つの処理モデルがあります。

  • Item based processing - javax.batch.api.chunk.ItemReaderjavax.batch.api.chunk.ItemWriter、任意でjavax.batch.api.chunk.ItemProcessor
  • Task based processing - javax.batch.api.Batchletの実装。この処理モデルはorg.springframework.batch.core.step.tasklet.Taskletと同様。

1.5.1. Item based processing

このコンテキストにおけるitemベースの処理はItemReaderで読み込むアイテム数がセットするchunkサイズです。stepの設定には、item-count(デフォルト10)を指定し、任意でcheckpoint-policyをitem(デフォルト値)に設定します。

...
<step id="step1">
    <chunk checkpoint-policy="item" item-count="3">
        <reader ref="fooReader"/>
        <processor ref="fooProcessor"/>
        <writer ref="fooWriter"/>
    </chunk>
</step>
...

itemベースのチェックポイントを使う場合、time-limitも使えます。指定アイテム数を処理すべきタイムリミットを設定します。タイムアウトに達すると、chunkは読み込んだitemがあったとしても、item-countの設定に関わらず、完了します。

1.5.2. Custom checkpointing

JSR-352はstepのチェックポイント内のコミットインターバルで処理を呼び出します。上述の通りitemベースのチェックポイントはその1つです。しかし、多くの場合これは十分にロバストではありません。このため、仕様でjavax.batch.api.chunk.CheckpointAlgorithmインタフェースの実装によりカスタムチェックポイントを作成可能です。この機能はSpring Batchのカスタムcompletion policyと同等です。CheckpointAlgorithmの実装を使うには、checkpoint-policyを、以下のようにstepのfooCheckpointerCheckpointAlgorithmの実装を参照します。

...
<step id="step1">
    <chunk checkpoint-policy="custom">
        <checkpoint-algorithm ref="fooCheckpointer"/>
        <reader ref="fooReader"/>
        <processor ref="fooProcessor"/>
        <writer ref="fooWriter"/>
    </chunk>
</step>
...

1.6. Running a job

JSR-352ベースのjob実行のエントリーポイントはjavax.batch.operations.JobOperatorです。Spring Batchにはこのインタフェースの実装(org.springframework.batch.core.jsr.launch.JsrJobOperator)があります。この実装クラスはjavax.batch.runtime.BatchRuntimeがロードします。JSR-352ベースのバッチジョブの起動は以下のように実装します。

JobOperator jobOperator = BatchRuntime.getJobOperator();
long jobExecutionId = jobOperator.start("fooJob", new Properties());

上記コードは以下を行います。

  • ベースApplicationContextの初期化 - バッチの各種機能を使えるように、フレームワークで基盤部分の初期化を行います。これはJVMごとに1回発生します。初期化コンポーネント@EnableBatchProcessingのそれと似ています。詳細はJsrJobOperatorjavadocを参照してください。
  • jobが要求するApplicationContextのロード - 上の例では、フレームワークは/META-INF/batch-jobsのfooJob.xmlを参照し、前に解説した共有コンテキストの子コンテキストとしてロードします。
  • job起動 - コンテキスト内に定義したjobを非同期に実行する。JobExecutionのidが返される。

※ すべてのJSR-352ベースのバッチジョブは非同期に実行します。

SimpleJobOperatorJobOperator#startを呼ぶ場合、Spring Batchは初回実行か以前の実行のリトライかを判断します。JSR-352のJobOperator#start(String jobXMLName, Properties jobParameters)を使う場合、フレームワークは常に新規JobInstanceを生成します(JSR-352のjobパラメータは一意性を持たない(JSR-352 job parameters are non-identifying))。jobをリスタートするには、JobOperator#restart(long executionId, Properties restartParameters)を使用して下さい。

1.7. Contexts

JSR-352には2つのコンテキストオブジェクトがあり、jobのメタデータにアクセスするものと、バッチアーティファクトからstepにアクセスします。javax.batch.runtime.context.JobContextjavax.batch.runtime.context.StepContextです。これらはstepレベルアーティファクトBatchlet, ItemReaderなど)で利用可能で、JobContextはjobレベルアーティファクトJobListenerなど)でも利用可能です。

カレントのスコープ内でJobContextStepContextの参照を得るには、@Injectを使います。

@Inject
JobContext jobContext;

@Autowire for JSR-352 contexts Springの@Autowireは上記コンテキストのインジェクションには使用出来ません。

Spring Batchでは、JobContextStepContextはこれらに対応するexecutionオブジェクト(JobExecutionStepExecution)をラップします。StepContext#setPersistentUserData(Serializable data)はSpring BatchのStepExecution#executionContextに保存します。

1.8. Step Flow

JSR-352ベースのjobの内部の、stepのflowはSpring Batchのそれと同様の動作します。ただし、多少微妙に異なる点があります。

  • Decision’s are steps - In a regular Spring Batch job, a decision is a state that does not have an independent StepExecution or any of the rights and responsibilities that go along with being a full step.. However, with JSR-352, a decision is a step just like any other and will behave just as any other steps (transactionality, it gets a StepExecution, etc). This means that they are treated the same as any other step on restarts as well.*5
  • next属性とstep transitions - Spring Batchのjobでは、これらは同一stepで一緒に使う事が可能です。JSR-352でも同一stepで使う事が可能で、next属性が評価において優先します。
  • transitions要素の順序 - Spring Batchのjobでは、transition要素は最も一致するものからしないものにソートしてその順序で評価します。JSR-352のjobsはXMLで指定した順序でtransition要素を評価します。

1.9. Scaling a JSR-352 batch job

Spring Batchには4つのスケーリングの方法があります(最後の2つは複数JVMで実行)。

  • Split - パラレルに複数stepを実行。
  • Multiple threads - 複数スレッドで単一stepを実行。
  • Partitioning - パラレル処理でデータを分割(master/slave)。
  • Remote Chunking - ロジックのprocessor pieceをリモートに実行。

JSR-352はバッチジョブのスケーリングに2つのオプションがあります。両オプションとも単一JVMのみをサポートします。

  • Split - Spring Batchのものと同等。
  • Partitioning - Spring Batchと概念的には同等だが微妙に実装は異なる。

1.9.1. Partitioning

概念的には、JSR-352のパーティショニングはSpring Batchと同等です。処理対象の入力の識別のためにメタデータが各スレーブに渡され、スレーブはマスターに処理結果を返します。しかし、いくつか重要な違いがあります。

  • Partitioned Batchlet - 複数スレッドで複数インスタンスBatchletを動かす。各インスタンスJSLもしくはPartitionPlanのプロパティをそれぞれ固有で持つ。
  • PartitionPlan - Spring Batchのパーティショニングでは、ExecutionContextを各パーティションに渡します。JSR-352では、単一のjavax.batch.api.partition.PartitionPlanメタデータProperties配列と共に各パーティションに渡します。
  • PartitionMapper - JSR-352はパーティションメタデータの生成に2種類の方法があります。1つ目はJSL (partition properties)です。2つ目はjavax.batch.api.partition.PartitionMapperの実装です。機能的には、このインタフェースはSpring Batchのorg.springframework.batch.core.partition.support.Partitionerと、パーティショニングのメタデータをプログラム的に生成する、という点で似ています。
  • StepExecutions - Spring Batchでは、パーティションstepはmaster/slaveで動作します。JSR-352でも同一設定で動作します。ただし、スレーブのstepはofficial StepExecutionsを取得しません。このため、JsrJobOperator#getStepExecutions(long jobExecutionId)はマスターにだけStepExecutionを返します。

※ 子StepExecutionsはジョブリポジトリには存在し、JobExplorerおよびSpring Batch Adminを介して利用可能です。

  • Compensating logic - Spring Batchがstepでパーティショニングのmaster/slaveを実装する場合、StepExecutionListenersで何らかの補正処理を実行可能です。しかし、JSR-352のslaveは、エラー発生時の補正処理と、動的なexit status設定が可能なように、コンポーネントのコレクションを渡します。コンポーネントは以下の通りです。
Artifact Interface Description
javax.batch.api.partition.PartitionCollector スレーブstepからマスターに情報を送り返す手段の提供。スレーブスレッドごとに1インスタンス
javax.batch.api.partition.PartitionAnalyzer PartitionCollectorが収集する情報と完了パーティションの結果ステータスを受け取るエンドポイント。
javax.batch.api.partition.PartitionReducer パーティションstepの補正ロジックの提供。

1.10. Testing

JSR-352ベースのjobはすべて非同期実行なため、job完了の確認が困難です。テスト用に、Spring Batchはorg.springframework.batch.test.JsrTestUtilsを提供します。このユーティリティークラスは、jobの開始・jobのリスタート・完了待ち、が出来ます。jobが完了するとJobExecutionを返します。

*1:あんま興味無いセクションなんで他にまして訳がテキトーな点に注意してください。

*2:The base context is not processed by the JSR-352 processors for things like property injection so no components requiring that additional processing should be configured there. が原文

*3:the devil is in the details. が原文。ぐぐれば分かるが「思わぬところに落とし穴」という意味合いの慣用句。オサレに訳せなかったんで直訳

*4:正確に訳すのなら「代替プロパティ」とかになるんだろうけど。まぁええわ。

*5:よくわからんかった