jBatchのお勉強のついでにThe Java EE 7 TutorialのThe Java EE 7 Tutorial:Batch Processing | Java EE Documentationの章をテキトーに訳した。
なお、ヒジョーにビミョーで機械翻訳よりはごましお程度にマシな精度な品質なので、日本語が歪んでいたり表記がブレいていたりしてもそこのところは容赦願いたい。
55 バッチ処理(Batch Processing)
この章は、バッチジョブの実行・実装・定義のサポートを提供する、Javaプラットフォームでのバッチアプリケーション(JSR-352)について解説します。バッチジョブとは、ユーザーとの対話操作無しに実行が可能なタスクのことです。このバッチフレームワークの構成要素は、XMLベースのジョブ定義言語、Java API、バッチランタイム、によって成り立っています。
大抵のエンタープライズアプリケーションは、ユーザーとの対話操作無しに実行が可能なタスクを含んでいます。それらのタスクは、定期的かリソース消費が少ない時間帯に実行されたり、ログファイル・データベース・画像のような大量データ処理を時には実行します。例としては、請求・レポート生成・データフォーマット変換・画像処理、など。これらのタスクはバッチジョブ(batch jobs)と呼ばれます。
バッチ処理とは、コンピューター上でバッチジョブを実行すること、を指します。Java EEは、すべてのバッチアプリケーションに共通なバッチ実行のための基盤を提供するバッチ処理フレームワークを含んでおり、それらは開発者がバッチアプリケーション上でのビジネスロジックに集中できるようにします。バッチフレームワークは、XMLベースのジョブ定義言語、バッチジョブの実行を管理するコンテナ、バッチコンテナを操作するためのインターフェースとクラス、から構成されています。
以降の章へのリンクは下記の通りです。*1
- バッチ処理入門 (Introduction to Batch Processing)
- Java EEでのバッチ処理 (Batch Processing in Java EE)
- 簡単な使用例(Simple Use Case)
- ジョブ定義言語の使用 (Using the Job Specification Language)
- バッチアーティファクトの作成 (Creating Batch Artifacts)
- バッチランタイムへのサブミット (Submitting Jobs to the Batch Runtime)
- バッチアプリケーションのパッケージ作成 (Packaging Batch Applications)
- The webserverlog Example Application
- The phonebilling Example Application
- Further Information about Batch Processing
55.1 バッチ処理入門 (Introduction to Batch Processing)
バッチジョブ(job)は、ユーザーが操作することなく完了できます。たとえば、通話料金請求アプリケーションはエンタープライズ情報システムから電話番号レコードを読み出し、アカウントごとに月次請求を生成します。このアプリケーションはユーザ操作を要求せず、バッチジョブとして動作します。
通話料金請求アプリケーションは2フェーズで構成されます。最初のフェーズでは、個々の発信情報と月次請求とを結び付け、次のフェースでは、各請求ごとに税金と合計金額を計算します。それぞれのフェーズがバッチジョブのステップ(step)です。
バッチアプリケーションはステップの集まりとそれらの実行順序によって定義します。他のバッチフレームワークによっては条件分岐のような追加要素があるかもしれないし、ステップのグループを並列実行させられるかもしれません。以降の章はステップの詳細について説明し、バッチフレームワークのその他の共通な特徴についての情報を提供します。
55.1.1 バッチジョブにおけるステップ(Steps in Batch Jobs)
ステップとは、バッチジョブの独立しているシーケンシャルなフェーズのことです。バッチジョブには、チャンク指向(chunk-oriented)とタスク指向(task-oriented)のステップが含まれます。
- チャンク指向のステップ(Chunk-oriented steps)とは、データソースから複数件読み込みによるデータ処理をし、それらのデータにビジネスロジックを適用し、それから結果を保存することです。チャンクステップは、一度に一件読み込みんで処理をし、その結果をグループ化してチャンクに送ります。結果はチャンクが設定サイズに達したときに保存されます。チャンク指向の処理は結果の保存を効率的にし、トランザクション境界の管理を容易にします。チャンクステップは三つのパートに分かれます。
- チャンクステップは、大量データ処理のためにしばしば長時間実行となります。バッチフレームワークは、チャンクステップがチェックポイント(checkpoints)を使用して処理の進行状況をブックマークできるようにします。中断されたチャンクステップは最後のチェックポイントから再開できます。チャンクステップの入力・検索と出力・書き込みパートは各チャンクの処理後に現在位置をセーブしておき、ステップの再開時に復元します。
Figure 55-1 バッチジョブにおけるチャンクステップ
Description of "Figure 55-1 Chunk Steps in a Batch Job"
- タスク指向のステップ(Task-oriented steps)とは、データソースのデータを使用する処理以外のタスク実行のことです。たとえば、ディレクトリの作成や削除、ファイルの移動、データベーステーブルの作成や削除、リソースの設定、などです。タスクステップは、チャンクステップに比べるとたいていは短時間です。
例えば、通話料金請求アプリケーションは二つのチャンクステップから構成されます。
- 最初のステップでは、入力・検索パートはレジストリから通話記録を読み出し、ビジネスプロセスパートは通話と請求とを結びつけてもしアカウントが存在しない場合には請求を作成*2し、そして出力・書き込みパートはデータベースに請求を保存します。
- 次のステップでは、入力・検索パートはデータベースから請求を読み出し、ビジネスプロセスパートでは税金と請求ごとの合計金額を計算し、最後に出力・書き込みパートはデータベースのレコードを更新して請求書を印刷可能にします。 このアプリケーションはまた、前月に生成された請求からファイルをクリーンナップするタスクステップも含みます
55.1.2 ステータスと条件分岐(Status and Decision Elements)*3
バッチフレームワークはジョブでのステップごとにステータス(status)を記録し続けます。ステータスはステップが実行中か完了したかを示します。もしステップが完了した場合、ステータスは次のうちいずれか一つを意味します。ステップの実行が正常終了した・ステップが中断された・エラーが発生した。
ステップに加えて、バッチジョブには条件分岐(decision elements)を作れます。条件分岐は、直前ステップの完了ステータスを使用して、次のステップを進めるかバッチジョブを終了させるか、といったことが出来ます。条件分岐はバッチジョブが終了したときのステータスを設定します。ステップと同様に、バッチジョブは正常終了することもあれば、中断インタラプトされたり失敗することもあります。
Figure 55-2は、チャンクステップ、タスクステップと条件武器を含むジョブの例を示しています。
Figure 55-2 ステップと条件分岐で構成されるジョブ
Description of "Figure 55-2 Steps and Decision Elements in a Job"
55.1.3 パラレル処理(Parallel Processing)
時にバッチジョブは大量データを処理したり、高負荷な処理を実行します。バッチアプリケーションは二つのシナリオにおいて並行処理から恩恵を受けられます。
- 互いに依存しないステップは異なるスレッドで実行可能です。
- 処理済アイテムの結果に依存せずに個々のアイテムに対して処理を行うチャンク指向ステップは複数のスレッドで実行可能です。 バッチフレームワークは、独立したステップをグループ化し、チャンク指向ステップを並行処理できるように分割するための仕組みを開発者に提供します。
55.1.4 バッチフレームワークの機能(Batch Framework Functionality)
バッチアプリケーションは以下のように共通の要求を持っています。
- ジョブ・ステップ・条件分岐およびそれらの関係を定義すること。
- ステップのグループ実行やステップのパートをパラレル処理すること。
- ジョブとステップのステータス情報と管理すること。
- ジョブの起動と中断されたジョブを再開すること。
- エラーハンドリング。
バッチフレームワークはすべてのバッチアプリケーションに共通の要求に応えるためのバッチ実行インフラを提供し、開発者をアプリケーションのビジネスロジックに集中出来るようにします。バッチフレームワークは、ジョブとステップを記述するためのフォーマット、API、バッチジョブの実行を管理するサービス、から構成されます。
55.2 Java EEでのバッチ処理 (Batch Processing in Java EE)
この章ではJava EEのバッチ処理フレームワークのコンポーネント一覧とバッチアプリケーション開発に必要なステップの概要について解説します。
55.2.1 バッチ処理フレームワーク(The Batch Processing Framework)
Java EEのバッチ処理フレームワークは以下の要素から構成されます。
- ジョブの実行を管理するバッチランタイム。
- XMLベースのジョブ定義言語。
- バッチランタイム操作用のJava API。
- ステップ、条件分岐、その他バッチアーティファクトを実装するためのJava API。
Java EEのバッチアプリケーションはXMLファイルとJavaクラスから構成されます。XMLファイルはバッチアーティファクトを一単位とするジョブの構造とそれらの関係を定義します。(バッチアーティファクトとは、チャンク指向ステップ・タスク指向ステップ・条件分岐およびその他のバッチアプリケーションのコンポーネントの一部分のことです。)JavaクラスはXMLファイルで定義されたバッチアーティファクトのアプリケーションロジックを実装します。バッチランタイムはXMLファイルをパースし、バッチアプリケーションでジョブを実行するためにJavaクラスとしてバッチアーティファクトをロードします。
55.2.2 バッチアプリケーションの作成(Creating Batch Applications)
Java EEでバッチアプリケーションを作成するための手順は以下の通りです。
- バッチアプリケーションの設計
- 入力データの入手元とフォーマット、要求される処理結果、処理内容の設計。
- アプリケーションのジョブを、チャンク指向ステップ・タスク指向ステップ・条件分岐によって構成し、それらの間の依存関係を決定する。
- ステップ遷移を一単位とする実行順序を決定する。
- パラレル実行可能なステップと一つ以上のスレッドで実行可能なステップを特定する。
- ステップや条件分岐などをフレームワークが指定するインターフェースを実装したJavaクラスとしてバッチアーティファクトを作成する。それらのJavaクラスに含まれるコードは、入力ソースからデータを読み込み、何らかの処理を行って結果を保存します。バッチアーティファクトはDIを使用してバッチランタイムからコンテキストオブジェクトを取得可能です。
- ジョブ定義言語を使用してジョブとステップの実行フローをXMLファイルで定義します。XMLファイルの要素はJavaクラスとして実装されたバッチアーティファクトを参照します。バッチアーティファクトは、XMLファイルで定義されたファイル名やデータベースなどのプロパティにアクセス可能です。
- バッチランタイムが提供するJava APIを使用してバッチアプリケーションを実行します。
以降の節では、バッチアプリケーションを作成するためのJava EEのバッチ処理フレームワークのコンポーネントの使用方法の詳細について解説します。
55.2.3 バッチジョブの要素(Elements of a Batch Job)
バッチジョブは以下の要素のうち一つ以上から構成されます。
- ステップ(Steps)
- フロー(Flows)
- スプリット(Splits)
- 条件分岐(Decision elements)
バッチ処理入門 (Introduction to Batch Processing)で解説したステップは、チャンク指向にもタスク指向にも出来ます。チャンク指向ステップはパーティーションステップ(partitioned steps)として定義できます。パーティーションチャンクステップでは、あるアイテムの処理をするとき他のアイテムに依存しないため、ステップは一つ以上のスレッドで実行可能です。
フロー(flow)とは、一単位として実行されるステップのシーケンスです。関連するステップのシーケンスはグループ化されて一つのフローになります。フローのステップは、そのフロー外のステップへは遷移出来ません。フローの最終ステップが完了したとき、フローは次の要素へと遷移します。
スプリット(split)とは、パラレルに実行するフローの組み合わせです。フローはそれぞれ別のスレッドで実行されます。すべてのフローが完了したとき、スプリットは次に要素へと遷移します。
条件分岐は、前のステップの完了ステータスを使用して、次のステップに進むかバッチジョブを終了するかを決定します。
55.2.4 プロパティとパラメーター(Properties and Parameters)
ジョブとステップはプロパティを持てます。ジョブ定義ファイルにプロパティを定義し、バッチアーティファクトはバッチランタイムからコンテキストオブジェクトを使用してプロパティを取得できます。プロパティを使用することで、ビジネスロジックからジョブの静的なパラメーターを分離し、異なるジョブ定義ファイルでバッチアーティファクトが再利用可能になります。
「55.4 ジョブ定義言語の使用」で、プロパティの詳細について、「55.5 バッチアーティファクトの作成」でプロパティの取得方法について、解説します。
Java EEアプリケーションはまた、バッチランタイムへジョブがサブミットされたとき、ジョブへパラメーター(parameters)を渡せます。これにより、実行時にのみ有効な動的パラメーターを定義可能です。パラメータはまた、パーティーションステップに必要不可欠で、たとえば各パーティーションが要求する処理範囲のために使用されます。
「55.6 バッチランタイムへのサブミット」で、ジョブがサブミットされるときのパラメータについて解説します。「55.9 The phonebilling Example Application」で、パーティーションステップ用のパラメータ定義とバッチアーティファクトでのパラメータ取得方法について解説します。
55.2.5 ジョブインスタンスと実行(Job Instances and Job Executions)
ジョブ定義は複数のインスタンス(instances)と各インスタンスごとに異なるパラメータを持てます。ジョブの実行(execution)とは、ジョブインスタンスを実行することを指します。バッチランタイムは、「55.6.2 Checking the Status of a Job」で解説されるように、ジョブ実行とジョブインスタンスについての情報をメンテナンスしています。
55.2.6 バッチと完了ステータス(Batch and Exit Status)
ジョブのステータス、ステップ、スプリット、フローはバッチランタイム上ではバッチステータス(batch status)として表現されます。バッチステータスの値はTable 55-1に示すとおりで、これらは文字列です。
Table 55-1 バッチステータスの値
値 | 説明 |
---|---|
STARTING |
ジョブがバッチランタイムへサブミット済 |
STARTED |
ジョブが実行中 |
STOPPING |
ジョブが停止命令を受け取り済 |
STOPPED |
ジョブが停止済 |
FAILED |
ジョブがエラー終了 |
COMPLETED |
ジョブが正常終了 |
ABANDONED |
破棄されたジョブ |
「55.6 バッチランタイムへのサブミット」で解説されるように、Java EEアプリケーションはJobOperator
インターフェースを使用してジョブのバッチステータスを取得できます。「55.4 ジョブ定義言語の使用」で解説されるように、ジョブ定義ファイルはジョブ定義言語(JSL:Job Specification Language)を使用してバッチステータスを参照できます。「55.5.3 Using the Context Objects from the Batch Runtime」で解説されるように、バッチアーティファクトはコンテキストオブジェクトを使用してバッチステータスを取得できます。
フローでは、バッチステータスはフローでの最終ステップのものを指します。スプリットでは、バッチステータスは以下の通りです。
COMPLETED
- 全フローがCOMPLETED
である。FAILED
- フローのいずれかがFAILED
である。STOPPED
- フローのいずれかがSTOPPED
であり、FAILED
が一つも無い。
ジョブ・ステップ・スプリット・フローのバッチステータスはバッチランタイムによって設定されます。ジョブ・ステップ・スプリット・フローはまた、バッチステータスベースのユーザ定義値である完了ステータス(exit status)持てます。完了ステータスはジョブ定義ファイルもしくはバッチアーティファクトで設定出来ます。それらの完了ステータスは、先に述べたバッチステータスと同様の方法で取得できます。完了ステータスのデフォルト値はバッチステータスと同じです。
55.3 簡単な使用例(Simple Use Case)
この章では、ジョブ定義言語(JSL)を使用したジョブとバッチアーティファクト定義の簡単な使用例について解説します。バッチフレームワークの要素の詳細についてはこの章の他の節を参照してください。
以下は、チャンクステップとタスクステップのジョブ定義です。
<?xml version="1.0" encoding="UTF-8"?> <job id="simplejob" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0"> <properties> <property name="input_file" value="input.txt"/> <property name="output_file" value="output.txt"/> </properties> <step id="mychunk" next="mytask"> <chunk> <reader ref="MyReader"></reader> <processor ref="MyProcessor"></processor> <writer ref="MyWriter"></writer> </chunk> </step> <step id="mytask"> <batchlet ref="MyBatchlet"></batchlet> <end on="COMPLETED"/> </step> </job>
55.3.1 チャンクステップ(Chunk Step)
多くの場合、チャンク指向ステップのためにチェックポイントクラスを実装すべきです。以下のクラスは、テキストファイルの列番号を保持します。
public class MyCheckpoint implements Serializable { private long lineNum = 0; public void increase() { lineNum++; } public long getLineNum() { return lineNum; } }
以下のItemReader
実装クラスは、ジョブが再開された場合には与えられたチェックポイントから入力ファイルの読み込みを続行します。アイテム*4はテキストファイルの各行に対応します。より複雑なシナリオでは、アイテムは独自のJavaクラスで入力元はデータベース等になります。
@Dependent @Named("MyReader") public class MyReader implements javax.batch.api.chunk.ItemReader { private MyCheckpoint checkpoint; private BufferedReader breader; @Inject JobContext jobCtx; public MyReader() {} @Override public void open(Serializable ckpt) throws Exception { if (ckpt == null) checkpoint = new MyCheckpoint(); else checkpoint = (MyCheckpoint) ckpt; String fileName = jobCtx.getProperties() .getProperty("input_file"); breader = new BufferedReader(new FileReader(fileName)); for (long i=0; i<checkpoint.getLineNum(); i++) breader.readLine(); } @Override public void close() throws Exception { breader.close(); } @Override public Object readItem() throws Exception { String line = breader.readLine(); return line; } }
このケースでは、ItemProcessor
の実装クラスは読み込んだ行を大文字に変換するだけです。より複雑な例では、様々なやり方でアイテムを処理したり、独自のJavaクラスへ変換したりします。
@Dependent @Named("MyProcessor") public class MyProcessor implements javax.batch.api.chunk.ItemProcessor { public MyProcessor() {} @Override public Object processItem(Object obj) throws Exception { String line = (String) obj; return line.toUpperCase(); } }
Note:バッチ処理APIはジェネリクスをサポートしません。多くの場合、processItem
の引数を適切な型へとキャストしなければなりません。
ItemWriter
の実装クラスはItemProcessor
を通過したデータをファイルへ出力します。もしチェックポイントが無ければ出力ファイルを上書きし、そうでなければ、ファイルのEOFへの書き込みを再開します。アイテムはチャンクごとに書き込まれます。
@Dependent @Named("MyWriter") public class MyWriter implements javax.batch.api.chunk.ItemWriter { private BufferedWriter bwriter; @Inject private JobContext jobCtx; @Override public void open(Serializable ckpt) throws Exception { String fileName = jobCtx.getProperties() .getProperty("output_file"); bwriter = new BufferedWriter(new FileWriter(fileName, (ckpt != null))); } @Override public void writeItems(List<Object> items) throws Exception { for (int i = 0; i < items.size(); i++) { String line = (String) items.get(i); bwriter.write(line); bwriter.newLine(); } } @Override public Serializable checkpointInfo() throws Exception { return new MyCheckpoint(); }
55.3.2 タスクステップ(Task Step)
タスクステップは出力ファイルのサイズを表示します。より複雑なシナリオでは、タスクステップはチャンク指向ではうまく扱えないタスクを実行します。
@Dependent @Named("MyBatchlet") public class MyBatchlet implements javax.batch.api.chunk.Batchlet { @Inject private JobContext jobCtx; @Override public String process() throws Exception { String fileName = jobCtx.getProperties() .getProperty("output_file"); System.out.println(""+(new File(fileName)).length()); return "COMPLETED"; } }
55.4 ジョブ定義言語の使用 (Using the Job Specification Language)
ジョブ定義言語(JSL)は、XMLファイルでジョブのステップと実行順序を定義します。以下の例は、一つのチャンクステップと一つのタスクステップから構成されるジョブの定義方法を示しています。
<job id="loganalysis" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0"> <properties> <property name="input_file" value="input1.txt"/> <property name="output_file" value="output2.txt"/> </properties> <step id="logprocessor" next="cleanup"> <chunk checkpoint-policy="item" item-count="10"> <reader ref="com.xyz.pkg.LogItemReader"></reader> <processor ref="com.xyz.pkg.LogItemProcessor"></processor> <writer ref="com.xyz.pkg.LogItemWriter"></writer> </chunk> </step> <step id="cleanup"> <batchlet ref="com.xyz.pkg.CleanUp"></batchlet> <end on="COMPLETED"/> </step> </job>
この例は、loganalysis
バッチジョブを定義しています。このジョブは、logprocessor
チャンクステップとcleanup
タスクステップから構成されます。logprocessor
ステップはcleanup
ステップに遷移し、処理が完了したらジョブを終了します。
job
要素は二つのプロパティinput_file
とoutput_file
を定義しています。プロパティ定義を使用することで、Javaのバッチアーティファクトを再コンパイルすることなく異なる設定パラメータでバッチジョブを実行できます。バッチアーティファクトはバッチランタイムのコンテキストオブジェクトを使用してそれらのプロパティを取得できます。
logprocessor
ステップは、読み込みのためのLogItemReader
、ビジネスロジックのためのLogItemProcessor
、書き込みのためのLogItemWriter
、のバッチアーティファクトから構成されるチャンクステップです。このステップは10アイテムを処理するごとにチェックポイントを作成します。
cleanup
ステップは、CleanUp
クラスとして定義されるバッチアーティファクトから成るタスクステップです。ジョブはこのステップが完了したときに終了します。
次の節ではジョブ定義言語(JSL)の詳細について解説し、よく使う属性と子要素について示します。
55.4.1 ジョブ要素(The job Element)
job
要素は、常にジョブ定義ファイルのトップレベル要素です。主要な属性はid
とrestartable
です。job
要素は、一つのproperties
要素と、ゼロ個以上のlistener
、step
、flow
、split
要素を持つことが出来ます。例として、
<job id="jobname" restartable="true"> <listeners> <listener ref="com.xyz.pkg.ListenerBatchArtifact"/> </listeners> <properties> <property name="propertyName1" value="propertyValue1"/> <property name="propertyName2" value="propertyValue2"/> </properties> <step ...> ... </step> <step ...> ... </step> <decision ...> ... </decision> <flow ...> ... </flow> <split ...> ... </split> </job>
listener
要素は、ジョブの実行前後に呼び出されるメソッドを持つバッチアーティファクトを定義します。バッチアーティファクトはjavax.batch.api.listener.JobListener
インタフェースの実装です。ジョブリスナー実装の例は The webserverlog Example Application の The webserverlog Example Application を参照してください。
job
要素の内側の最初のstep
、flow
、split
要素が最初に実行されます。
55.4.2 ステップ要素(The step Element)
step
要素は、job
とflow
の子要素になれます。主要な属性はid
とnext
です。step
要素は以下の要素を持つことが出来ます。
- チャンク指向ステップ用の
chunk
要素一つか、タスク指向ステップ用ののbatchlet
要素一つ。 - 一つの
properties
要素(optional)。この要素は、バッチアーティファクトがバッチコンテキストオブジェクトを使用して取得できる、プロパティの組み合わせを定義します。 - 一つの
listener
要素(optional)。複数のリスナーを定義する場合はlisteners
要素。この要素は、ステップ実行の様々なフェーズをインターセプトするリスナーアーティファクトを定義します。チャンクステップでは、リスナーのバッチアーティファクトは以下のインターフェースを実装します。
StepListener
,ItemReadListener
,ItemProcessListener
,ItemWriteListener
,ChunkListener
,RetryReadListener
,RetryProcessListener
,RetryWriteListener
,SkipReadListener
,SkipProcessListener
,SkipWriteListener
.
タスクステップでは、リスナーのバッチアーティファクトはStepListener
インタフェースを実装します。
リスナー実装の例はThe webserverlog Example ApplicationのThe Listener Batch Artifactsを参照してください。 - 一つの
partition
要素(optional)。この要素は、一つ以上のスレッドで実行されるpartitionedステップのために使用します。 - 一つの
end
要素。そのステップがジョブの最後の要素の場合に使用します。この要素はバッチステータスをCOMPLETED
に設定します。 - 一つの
stop
要素(optional)。そのステップでジョブをstopする場合に使用します。この要素はバッチステータスをSTOPPED
に設定します。 - 一つの
fail
要素(optional)。そのステップでジョブをterminateする場合に使用します。この要素はバッチステータスをFAILED
に設定します。 - 一つ以上の
next
要素。next
属性が指定されていない場合に使用されます。この要素は、完了ステータスと関連付け、別のステップ、フロー、スプリットあるいは条件分岐を参照します。 以下はチャンクステップの例です。
<step id="stepA" next="stepB"> <properties> ... </properties> <listeners> <listener ref="MyItemReadListenerImpl"/> ... </listeners> <chunk ...> ... </chunk> <partition> ... </partition> <end on="COMPLETED" exit-status="MY_COMPLETED_EXIT_STATUS"/> <stop on="MY_TEMP_ISSUE_EXIST_STATUS" restart="step0"/> <fail on="MY_ERROR_EXIT_STATUS" exit-status="MY_ERROR_EXIT_STATUS"/> </step>
以下はタスクステップの例です。
<step id="stepB" next="stepC"> <batchlet ...> ... </batchlet> <properties> ... </properties> <listener ref="MyStepListenerImpl"/> </step>
55.4.2.1 チャンク要素(The chunk Element)
チャンク指向ステップ用に、step
要素の子要素にchunk
を作れます。この要素の属性一覧をTable 55-2に示します。
Table 55-2 チャンク要素の属性(Attributes of the chunk Element)
属性名 | 説明 | デフォルト値 |
---|---|---|
checkpoint-policy |
チャンクごとの処理結果のコミット方法を指定します。 ・ "item" - チャンクはitem-count 数処理後にコミットされます。・ "custom" - チャンクはcheckpoint-algorithm 要素で指定されるチェックポイントアルゴリズムに従ってコミットされます。チェックポイントは、チャンクの結果がコミットされた時に更新されます。すべてのチャンクはJava EEのグローバルトランザクションで処理されます。もし、チャンク中の一アイテムが処理に失敗した場合、トランザクションはロールバックし、このチャンクで処理されたアイテムは保存されません。 |
"item" |
item-count |
チャンクがコミットされてチェックポイントが更新される処理数を指定します。 | 10 |
time-limit |
checkpoint-policy="item" のとき、チャンクがコミットされてチェックポイントが更新される秒数を指定します。もし、 time-limit 秒で処理されていないitem-count アイテムがある場合、チャンクはコミットされてチェックポイントが更新されます。 |
0 (無制限) |
buffer-items |
チェックポイントを更新するまで処理されたアイテムをバッファするかどうかを指定します。もしtrueの場合、チャンクがコミットされてチェックポイントが更新されるまえに、バッファされたアイテムのリストで作られたアイテムライターが一度だけ呼び出されます*5。 | true |
skip-limit |
チャンク処理中にそのステップでスキップするスキップ可能例外(skippable exceptions)の数を指定します。スキップ可能例外クラスはskippable-exception-classes 要素で指定します。 |
無制限 |
retry-limit |
リトライ可能例外(retryable exceptions)が発生したときの、ステップの試行回数を指定します。リトライ可能例外クラスはretryable-exception-classes 要素で指定します。 |
無制限 |
chunk
要素は以下の要素から構成されます。
- 一つの
reader
要素。ItemReader
インターフェースを実装したバッチアーティファクトを指定します。 - 一つの
processor
要素。ItemProcessor
インタフェースを実装したバッチアーティファクトを指定します。 - 一つの
writer
要素。ItemWriter
インタフェースを実装したバッチアーティファクトを指定します。 - 一つの
checkpoint-algorithm
要素(optional)。CheckpointAlgorithm
インタフェースを実装したバッチアーティファクトを指定し、checkpoint-policyをcustomにする時に使用します。 - 一つ
skippable-exception-classes
要素(optional)。
この要素は、チャンク処理をスキップすべき、reader・writer・processorバッチアーティファクトからスローされる例外の組み合わせを指定します。chunk
要素のskip-limit
属性にはスキップする例外の最大数を指定します。 - 一つの
retryable-exception-classes
要素(optional)。
この要素は、チャンク処理をリトライするために、reader・writer・processorバッチアーティファクトからスローされる例外の組み合わせを指定します。chunk
要素のretry-limit
属性には最大試行数を指定します。 - 一つの
no-rollback-exception-classes
要素(optional)。この要素は、バッチランタイムが現在のチャンクをロールバックし、ロールバック無しで現在のオペレーションをリトライしないように、reader・writer・processorバッチアーティファクトからスローされる例外の組み合わせを指定します。
この要素で指定されない例外では、例外発生時には現在のチャンクはデフォルトでロールバックされます。
以下にチャンク指向ステップの例を示します。
<step id="stepC" next="stepD"> <chunk checkpoint-policy="item" item-count="5" time-limit="180" buffer-items="true" skip-limit="10" retry-limit="3"> <reader ref="pkg.MyItemReaderImpl"></reader> <processor ref="pkg.MyItemProcessorImpl"></processor> <writer ref="pkg.MyItemWriterImpl"></writer> <skippable-exception-classes> <include class="pkg.MyItemException"/> <exclude class="pkg.MyItemSeriousSubException"/> </skippable-exception-classes> <retryable-exception-classes> <include class="pkg.MyResourceTempUnavailable"/> </retryable-exception-classes> </chunk> </step>
この例はreader・processor・writerアーティファクトを持つチャンクステップを定義しています。ステップはチェックポイントを更新し、5アイテムを処理するごとにチャンクをコミットします。MyItemSeriousSubException
を除いて、MyItemException
とすべてのサブタイプの例外を最大10回までスキップします。MyResourceTempUnavailable
例外が発生したとき、最大3回までステップはチャンクをリトライします。
55.4.2.2 batchlet要素(The batchlet Element)
タスク指向ステップ用に、step
要素の子要素にbatchlet
を作れます。この要素の属性はref
のみで、Batchlet
インターフェースを実装したバッチアーティファクトを指定します。batchlet
*6要素はproperties
要素を持てます。
以下はタスク指向ステップの例です。
<step id="stepD" next="stepE"> <batchlet ref="pkg.MyBatchletImpl"> <properties> <property name="pname" value="pvalue"/> </properties> </batchlet> </step>
この例はバッチアーティファクトを指定したバッチステップを定義しています。
55.4.2.3 パーティーション要素(The partition Element)
partition
要素はstep
の子要素です。この要素はステップがパーティーションであることを示しています。多くのパーティーション・ステップは、処理済みアイテムの結果に依存しないアイテムを処理するチャンクステップです。ステップのパーティーション数を指定し、処理対象アイテムに固有の情報をパーティーションごとに与えます。たとえば、
- アイテムの範囲。たとえば、パーティーション1は、1から500を処理し、パーテーション2は、501から1000を処理する。
- 入力ソース。たとえば、パーティーション1は
input1.txt
のアイテムを処理し、パーティーション2はinput2.txt
のアイテムを処理する。
パーティーション数、アイテム数、パーティーション・ステップ用の入力ソースが開発やデプロイ時に分かっている場合、ジョブ定義ファイルにパーティーション固有の情報をプロパティとして設定し、ステップのバッチアーティファクトでプロパティを取得します。ランタイムは、ステップのバッチアーティファクト(reader, processor, writer)をパーティーションとしてインスタンスを多数生成し、アーティファクトのインスタンスはパーティーションに固有のプロパティを受け取ります。
多くの場合、パーティーション数、アイテム数、パーティーション・ステップ用の入力ソースは実行時にのみ決定されます。ジョブ定義ファイルでパーティーション固有のプロパティを静的に指定する代わりに、実行時に任意のデーターソースにアクセスするバッチアーティファクトを作成し、必要とされるパーティーション数とパーティーションごとに処理すべきアイテムの範囲を決められます。このバッチアーティファクトはPartitionMapper
インターフェースを実装します。バッチランタイムはこのアーティファクトを実行し、パーティーション用のステップのバッチアーティファクト(reader, writer, processor)をインスタンス化する情報を使用して、パーティーション固有のデータを引数としてバッチアーティファクトに渡します。
この節の残りはpartition
要素の詳細について説明し、ジョブ定義ファイルの例を二つ示します。一つはパーティションごとにアイテムの範囲をパーティーション用プロパティで指定し、もう一つはパーティーション固有情報を指定するためにcode>PartitionMapper```の実装を使用します。
パーティーションチャンクステップの複雑な例はThe phonebilling Example ApplicationのThe Phone Billing Chunk Stepを参照してください。
partition
要素は以下の要素を持つことができます。
- 一つの
plan
要素、ただしmapper
要素が無い場合。
この要素は、パーティーションごとのプロパティ・パーティーション数・スレッド数を、ジョブ定義ファイルで指定します。plan
要素は設定値が開発かデプロイ時に分かっている場合に役立ちます。 - 一つの
mapper
要素、ただしplan
要素が無い場合。
この要素は、パーティーションごとのプロパティ・パーティーション数・スレッド数を与えるバッチアーティファクトを指定します。バッチアーティファクトはPartitionMapper
インタフェースの実装です。パーティーションごとに要求される情報が実行時にのみ判明するときにこのオプションを使用します。 - 一つの
reducer
要素(optional)。
この要素はパーティーションステップの開始・終了・ロールバック時のコントロールを実行するバッチアーティファクトを指定します。異なるパーティーションからの結果をマージしたり他の関連オペレーションを実行したりすることができます。このバッチアーティファクトはPartitionReducer
インターフェースを実装します。 - 一つの
collector
要素(optional)。
この要素は各パーティーションからanalyzerパーティーションへ中間結果を送出するバッチアーティファクトを指定します。このバッチアーティファクトはタスクステップの終端とチャンクステップの各チェックポイント通過後に中間結果を送出します。このバッチアーティファクトはPartitionCollector
インターフェースを実装します。 - 一つの
analyzer
要素(optional)。この要素はcollectorパーティションのインスタンスからの中間結果を分析するバッチアーティファクトを指定します。このバッチアーティファクトはPartitionAnalyzer
インターフェースを実装します。 以下はplan
要素を使用したパーティーションステップの例です。
<step id="stepE" next="stepF"> <chunk> <reader ...></reader> <processor ...></processor> <writer ...></writer> </chunk> <partition> <plan partitions="2" threads="2"> <properties partition="0"> <property name="firstItem" value="0"/> <property name="lastItem" value="500"/> </properties> <properties partition="1"> <property name="firstItem" value="501"/> <property name="lastItem" value="999"/> </properties> </plan> </partition> <reducer ref="MyPartitionReducerImpl"/> <collector ref="MyPartitionCollectorImpl"/> <analyzer ref="MyPartitionAnalyzerImpl"/> </step>
この例では、plan
要素はジョブ定義ファイルで各パーティーションごとのプロパティを指定しています。
以下の例はplan
要素の代わりにmapper
を使用しています。PartitionMapper
の実装がジョブ定義ファイルでplan
要素が提供する情報と同様のことを動的に実現しています。
<step id="stepE" next="stepF"> <chunk> <reader ...></reader> <processor ...></processor> <writer ...></writer> </chunk> <partition> <mapper ref="MyPartitionMapperImpl"/> <reducer ref="MyPartitionReducerImpl"/> <collector ref="MyPartitionCollectorImpl"/> <analyzer ref="MyPartitionAnalyzerImpl"/> </partition> </step>
PartitionMapper
インターフェースの実装例はThe phonebilling Example Applicationを参照してください。
55.4.3 フロー要素(The flow Element)
flow
要素は、job
・flow
・split
の子要素にできます。属性はid
とnext
です。フローは、フロー・ステップ・スプリット・条件分岐に遷移できます。flow
要素は以下の要素を含むことができます。
- 一つ以上の
step
要素。 - 一つ以上の
flow
要素 (optional)。 - 一つ以上の
split
要素 (optional)。 - 一つ以上の
decision
要素 (optional)。
フロー内の最終step
はnext
属性もしくはnext
要素を持てません。ステップとフロー内の他の要素はフロー外の要素へは遷移できません。
以下がflow
要素の例です。
<flow id="flowA" next="stepE"> <step id="flowAstepA" next="flowAstepB">...</step> <step id="flowAstepB" next="flowAflowC">...</step> <flow id="flowAflowC" next="flowAsplitD">...</flow> <split id="flowAsplitD" next="flowAstepE">...</split> <step id="flowAstepE">...</step> </flow>
この例のフローは、三つのステップ・一つのフロー・一つのスプリットから構成されています。最終ステップはnext
属性を持ちません。最終ステップが完了したとき、このフローはstepE
へと遷移します。
55.4.4 スプリット要素(The split Element)
split
要素は、job
とflow
の子要素にできます。属性はid
とnext
です。スプリットは、スプリット・ステップ・フロー・条件分岐に遷移できます。split
要素は一つ以上のflow
要素のみ持つことが出来ます。そのflow
要素はスプリット内の他のflow
要素へのみ遷移できます。
以下は三つのフローがコンカレントに実行されるスプリットの例です。
<split id="splitA" next="stepB"> <flow id="splitAflowA">...</flow> <flow id="splitAflowB">...</flow> <flow id="splitAflowC">...</flow> </split>
55.4.5 条件分岐要素(The decision Element)
decision
要素は、job
とflow
の子要素にできます。属性はid
とnext
です。ステップ・フロー・スプリットはdecision
要素に遷移できます。この要素は以前のステップ・フロー・スプリットの実行情報に基づいて次のステップ・フロー・スプリットを決定するためのバッチアーティファクトを指定します。バッチアーティファクトはDecider
インタフェースを実装します。decision
要素は以下の要素を含められます。
- 一つ以上の
end
要素(optional)。
この要素はバッチステータスをCOMPLETED
に設定します。 - 一つ以上の
stop
要素(optional)。
この要素はバッチステータスをSTOPPED
に設定します。 - 一つ以上の
fail
要素(optional)。
この要素はバッチステータスをFAILED
に設定します。 - 一つ以上の
next
要素(optional)。 - 一つの
properties
要素(optional)。
以下はdecider
要素の例です。
<decision id="decisionA" ref="MyDeciderImpl"> <fail on="FAILED" exit-status="FAILED_AT_DECIDER"/> <end on="COMPLETED" exit-status="COMPLETED_AT_DECIDER"/> <stop on="MY_TEMP_ISSUE_EXIST_STATUS" restart="step2"/> </decision>
55.5 バッチアーティファクトの作成(Creating Batch Artifacts)
ジョブ定義言語(JSL)を使用してバッチアーティファクト単位でジョブを定義し終わったら、javax.batch.api
とそのサブパッケージのインターフェースを実装するJavaクラスとしてアーティファクトを作成します。
この節は主なバッチアーティファクトのインタフェースをリストアップし、バッチランタイムからコンテキストオブジェクトにアクセスする方法と、いくつかの例を紹介します。
55.5.1 バッチアーティファクトのインターフェース(Batch Artifact Interfaces)
以下の表はバッチアーティファクトを作成するために実装する必要のあるインターフェースの一覧です。インターフェースの実装はジョブ定義言語の使用で説明した要素から参照します。
Table 55-3はチャンクステップ・タスクステップ・条件分岐のバッチアーティファクトが実装するインターフェースの一覧です。
Table 55-4はパーティーションステップのバッチアーティファクトが実装するインターフェースの一覧です。
Table 55-5はジョブとステップリスナーのバッチアーティファクトが実装するインターフェースの一覧です。
Table 55-3 主なバッチアーティファクトのインターフェース(Main Batch Artifact Interfaces)
パッケージ | インターフェース | 説明 |
---|---|---|
javax.batch.api |
Batchlet |
タスク指向ステップのビジネスロジックを実装します。batchlet 要素で参照します。 |
javax.batch.api |
Decider |
以前のステップ・フロー・スプリットの実行情報に基づいて次のステップ・フロー・スプリットを決定します。decision 要素で参照します。 |
javax.batch.api.chunk |
CheckPointAlgorithm |
チャンクステップ用のカスタマイズしたチェックポイントポリシーを実装します。chunk 要素内のcheckpoint-algorithm 要素で参照します。 |
javax.batch.api.chunk |
ItemReader |
チャンクステップで入力ソースからアイテムを読み込みます。chunk 要素内のreader 要素から参照します。 |
javax.batch.api.chunk |
ItemProcessor |
チャンクステップで出力を得るために入力アイテムを処理します。chunk 要素内のprocessor 要素から参照します。 |
javax.batch.api.chunk |
ItemWriter |
チャンクステップでアイテムを出力するアイテムを書き込みます。chunk 要素内のwriter 要素から参照します。 |
Table 55-4 パーティーションのバッチアーティファクトのインタフェース(Partition Batch Artifact Interfaces)
パッケージ | インターフェース | 説明 |
---|---|---|
javax.batch.api.partition |
PartitionPlan |
パーティーション数・スレッド数・パーティーションごとのパラメータなど、パーティーションステップの実行方法を提供します。このアーティファクトはジョブ定義ファイルから直接参照されません。 |
javax.batch.api.partition |
PartitionMapper |
PartitionPlan オブジェクトを提供します。partition 要素内のmapper 要素から参照します。 |
javax.batch.api.partition |
PartitionReducer |
パーティーションステップが開始・終了・ロールバックしたときのコントロールを行います。partition 要素内のreducer 要素から参照します。 |
javax.batch.api.partition |
PartitionCollector |
中間結果を各パーティーションからanalyzerパーティーションへ送ります。partition 要素内のcollector 要素から参照します。 |
javax.batch.api.partition |
PartitionAnalyzer |
各パーティションからの最終処理結果を処理します。partition 要素内のanalyzer 要素から参照します。 |
Table 55-5 リスナーのバッチアーティファクトのインタフェース(Listener Batch Artifact Interfaces)
パッケージ | インターフェース | 説明 |
---|---|---|
javax.batch.api.listener |
JobListener |
ジョブの実行前後でインターセプトを実行します。job 要素内のlistener 要素から参照します。 |
javax.batch.api.listener |
StepListener |
ステップの実行前後でインターセプトを実行します。step 要素内のlistener 要素から参照します。 |
javax.batch.api.chunk.listener |
ChunkListener |
各チャンクステップの実行前後およびエラー時にインターセプトを実行します。step 要素内のlistener 要素から参照します。 |
javax.batch.api.chunk.listener |
ItemReadListener |
各アイテム読み込みの実行前後およびエラー時にインターセプトを実行します。step 要素内のlistener 要素から参照します。 |
javax.batch.api.chunk.listener |
ItemProcessListener |
各アイテム処理の実行前後およびエラー時にインターセプトを実行します。step 要素内のlistener 要素から参照します。 |
javax.batch.api.chunk.listener |
ItemWriteListener |
各アイテム書き込みの実行前後およびエラー時にインターセプトを実行します。step 要素内のlistener 要素から参照します。 |
javax.batch.api.chunk.listener |
RetryReadListener |
エラー発生時のアイテム読み込みリトライ時にインターセプトを実行します。step 要素内のlistener 要素から参照します。 |
javax.batch.api.chunk.listener |
RetryProcessListener |
エラー発生時のアイテム処理リトライ時にインターセプトを実行します。step 要素内のlistener 要素から参照します。 |
javax.batch.api.chunk.listener |
RetryWriteListener |
エラー発生時のアイテム書き込みリトライ時にインターセプトを実行します。step 要素内のlistener 要素から参照します。 |
javax.batch.api.chunk.listener |
SkipReadListener |
アイテム読み込みクラスでのスキップ可能例外のハンドリングをインターセプトします。step 要素内のlistener 要素から参照します。 |
javax.batch.api.chunk.listener |
SkipProcessListener |
アイテム処理クラスでのスキップ可能例外のハンドリングをインターセプトします。step 要素内のlistener 要素から参照します。 |
javax.batch.api.chunk.listener |
SkipWriteListener |
アイテム書き込みクラスでのスキップ可能例外のハンドリングをインターセプトします。step 要素内のlistener 要素から参照します。 |
55.5.2 バッチアーティファクトのDI(Dependency Injection in Batch Artifacts)
作成したバッチアーティファクトをCDIで動かすための手順を以下に示します。
@Named("MyItemReaderImpl") public class MyItemReaderImpl implements ItemReader { /* ... ItemReaderインタフェースのメソッドをオーバーライドする。 ... */ }
public MyItemReaderImpl() {}
<step id="stepA" next="stepB"> <chunk> <reader ref="MyItemReaderImpl"></reader> ... </chunk> </step>
- この例はバッチアーティファクトを指定するため、FQCN(com.xyz.pkg.MyItemReaderImpl)の代わりにCDIネーム(MyItemReaderImpl)を 使用しています。
javax.enterprise.context.Dependent
アノテーションをバッチアーティファクトに付与するか空のbeans.xml
を含めるかして、モジュールをCDIBeanアーカイブにします。
@Dependent @Named("MyItemReaderImpl") public class MyItemReaderImpl implements ItemReader { ... }
Beanアーカイブの詳細な情報については、Chapter 25, "Contexts and Dependency Injection for Java EE: Advanced Topics".のPackaging CDI Applicationsを参照してください。
Note:CDIはバッチアーティファクトでバッチランタイムからコンテキストオブジェクトを取得するために必要です。
以上の手順に従わない場合は下記のようなエラーが発生します。
55.5.3 バッチランタイムのコンテキストオブジェクトを使用する(Using the Context Objects from the Batch Runtime)
バッチランタイムはjavax.batch.runtime.context
パッケージのJobContext
とStepContext
インターフェースを実装するコンテキストオブジェクトを提供します。これらのオブジェクトは現在のジョブとステップに関連付けされ、下記の操作が可能です。
- 現在のジョブやステップ・その名前・インスタンスID・実行ID・バッチステータス・完了ステータスの取得。
- ユーザー定義完了ステータスの設定。
- ユーザーデータの保存。
- ジョブやステップのプロパティの取得。
reader, processor, writer, batchlet, listenerなどのバッチアーティファクト実装でバッチランタイムからコンテキストオブジェクトをDIできます。以下の例は、reader実装でジョブ定義ファイルのプロパティを取得する方法を示しています。
@Dependent @Named("MyItemReaderImpl") public class MyItemReaderImpl implements ItemReader { @Inject JobContext jobCtx; public MyItemReaderImpl() {} @Override public void open(Serializable checkpoint) throws Exception { String fileName = jobCtx.getProperties() .getProperty("log_file_name"); ... } ... }
DIを使用するためのバッチアーティファクトの設定方法についてはDependency Injection in Batch Artifactsを参照してください。
Noteアーティファクトのコンストラクタでバッチコンテキストオブジェクトを取得してはいけません。
バッチランタイムへジョブをサブミットするまでジョブは起動しないので、CDIがアプリケーション上でアーティファクトをインスタンス化するときバッチコンテキストオブジェクトは利用不可です。アーティファクトのインスタンス化に失敗し、アプリケーションがジョブをサブミットしてもバッチランタイムはバッチアーティファクトを見つけられません。
55.6 バッチランタイムへのジョブのサブミット(Submitting Jobs to the Batch Runtime)
javax.batch.operations
パッケージのJobOperator
インターフェースを使用してジョブのサブミットと情報取得をすることができます。このインタフェースは以下の機能を提供します。
- すべてのジョブ名の取得
- ジョブの開始・停止・再開・放棄(abandon)
- ジョブインスタンスの取得とジョブの実行
javax.batch.runtime
パッケージのBatchRuntime
クラスはJobOperator
オブジェクトを取得するためのgetJobOperator
ファクトリーメソッドを提供します。
55.6.1 ジョブの開始(Starting a Job)
以下のコード例はJobOperator
オブジェクトを取得してバッチジョブをサブミットする方法を示しています。
JobOperator jobOperator = BatchRuntime.getJobOperator(); Properties props = new Properties(); props.setProperty("parameter1", "value1"); ... long execID = jobOperator.start("simplejob", props);
JobOperator.start
メソッドの引数の一つ目はジョブ定義ファイルで指定したジョブ名です。二つ目は実行ジョブのパラメーターであるProperties
オブジェクトです。ジョブ実行時にのみ判明するパラメータについてはこのプロパティ引数を使用して渡せます。
55.6.2 ジョブステータスの確認(Checking the Status of a Job)
javax.batch.runtime
パッケージのJobExecution
インターフェースはサブミットされたジョブの情報を取得するメソッドを提供します。
- バッチと実行ジョブの完了ステータスの取得
- 実行開始・更新・終了時刻の取得
- ジョブ名の取得
- 実行IDの取得
以下のコード例は実行IDを使用してジョブのバッチステータスを取得する方法を示しています。
JobExecution jobExec = jobOperator.getJobExecution(execID); String status = jobExec.getBatchStatus().toString();
55.6.3 アプリケーションにおけるバッチランタイムの起動(Invoking the Batch Runtime in Your Application)
バッチランタイムを起動するコンポーネントは、特定のアプリケーションのアーキテクチャに依存します。たとえば、EJB・サーブレット・マネージドBeanなどからバッチランタイムを起動できます。
JSFのマネージドBeanからバッチランタイムを起動する方法の詳細はThe webserverlog Example ApplicationとThe phonebilling Example Applicationを参照してください。
55.7 バッチアプリケーションのパッケージ作成(Packaging Batch Applications)
ジョブ定義ファイルとバッチアーティファクトは異なるパッケージに分割出来ず*7、任意のJava EEアプリケーションに含めることが出来ます。
アプリケーションのクラスにバッチアーティファクトのクラスと、ジョブ定義ファイルを以下のディレクトリに配置して、パッケージを作成します。
jar
の場合はMETA-INF/batch-jobs/
war
の場合はWEB-INF/classes/META-INF/batch-jobs/
ジョブ定義ファイルの名前はジョブIDと一致する必要があります。たとえば、以下のようにジョブを定義した場合、
<?xml version="1.0" encoding="UTF-8"?> <job id="simplejob" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0"> ... </job>
ジョブ定義ファイルの名前をWEB-INF/classes/META-INF/batch-jobs/simplejob.xml
にして、war
アーカイブを作成します。
55.8 The webserverlog Example Application
tut-install/examples/batch/webserverlog/
*8からダウンロードできるwebserverlog
サンプルアプリケーションは、Java EEバッチフレームワークでwebサーバーログを分析する方法を紹介しています。このサンプルアプリケーションはログファイルを読み込み、タブレット端末からのPVの何パーセントが売上となったか、を調査します。
55.8.1 Architecture of the webserverlog Example Application
webserverlog
サンプルアプリケーションは以下の要素から構成されます。
- ジョブ定義言語(JSL)で記述したジョブ定義ファイル(
webserverlog.xml
)にチャンクステップとタスクステップからなるバッチジョブを定義。 - バッチジョブの入力となるログファイル(
log1.txt
)。 - チャンクステップの入力・出力アイテムを表現する二つのJavaクラス(
LogLine
とLogFilteredLine
)。 - チャンクステップの実装となる三つのバッチアーティファクト(
LogLineReader
,LogLineProcessor
,LogFilteredLineWriter
)。このステップはwebサーバーログを読み込み、クライアントの使用したブラウザでフィルタし、結果をテキストファイルに書き込み、を行います。 - リスナーの単純な実装となる二つのバッチアーティファクト(
InfoJobListener
とInfoItemProcessListener
)。 - バッチアーティファクト(
MobileBatchlet.java
)はフィルタされたログを基に統計データを計算します。 - バッチアプリケーションのフロントエンドとなる二つのJSFページ(
index.xhtml
とjobstarted.xhtml
)。前者のページはバッチジョブで処理予定のログファイルを表示し、後者のページはジョブのステータスと結果を表示します。 - JSFページから使用されるセッションBean(
JsfBean
)。バッチランタイムへジョブをサブミットし、ジョブのステータスをチェックし、最後にテキストファイルから結果を読み込みます。
55.8.1.1 The Job Definition File
ジョブ定義ファイルwebserverlog.xml
はWEB-INF/classes/META-INF/batch-jobs/
ディレクトリに配置されており、七つのジョブレベルのプロパティと二つのステップを持ちます。
<?xml version="1.0" encoding="UTF-8"?> <job id="webserverlog" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0"> <properties> <property name="log_file_name" value="log1.txt"/> <property name="filtered_file_name" value="filtered1.txt"/> <property name="num_browsers" value="2"/> <property name="browser_1" value="Tablet Browser D"/> <property name="browser_2" value="Tablet Browser E"/> <property name="buy_page" value="/auth/buy.html"/> <property name="out_file_name" value="result1.txt"/> </properties> <listeners> <listener ref="InfoJobListener"/> </listeners> <step id="mobilefilter" next="mobileanalyzer"> ... </step> <step id="mobileanalyzer"> ... </step> </job>
最初のステップは以下のように定義されています。
<step id="mobilefilter" next="mobileanalyzer"> <listeners> <listener ref="InfoItemProcessListeners"/> </listeners> <chunk checkpoint-policy="item" item-count="10"> <reader ref="LogLineReader"></reader> <processor ref="LogLineProcessor"></processor> <writer ref="LogFilteredLineWriter"></writer> </chunk> </step>
このステップは通常のチャンクステップで、ステップの各フェーズを実装するバッチアーティファクトが指定されています。バッチアーティファクトの参照は完全修飾クラス名(FQDN)ではなく、@Named
アノテーションをつけられたCDI Beanで指定されています。
次のステップは以下のように定義されています。
<step id="mobileanalyzer"> <batchlet ref="MobileBatchlet"></batchlet> <end on="COMPLETED"/> </step>
ステップには、タスクステップを実装するバッチアーティファクトが指定されています。これがジョブの最終ステップです。
55.8.1.2 The LogLine and LogFilteredLine Items
LogLine
クラスはwebサーバーログファイルのエントリを表現しており、以下のように定義されています。
public class LogLine { private String datetime; private String ipaddr; private String browser; private String url; /* ... Constructor, getters, and setters ... */ }
LogFileteredLine
クラスはこのクラスと似ているが、クライアントIPアドレスとURLの二つのフィールドのみ持ちます。
55.8.1.3 The Chunk Step Batch Artifacts
最初のステップは三つのバッチアーティファクトLogLineReader
とLogLineProcessor
とLogFilteredLineWriter
で構成されています。
LogLineReader
アーティファクトはwebサーバーログを読み込みます。
@Dependent @Named("LogLineReader") public class LogLineReader implements ItemReader { private ItemNumberCheckpoint checkpoint; private String fileName; private BufferedReader breader; @Inject private JobContext jobCtx; public LogLineReader() { } /* ... Override the open, close, readItem, and * checkpointInfo methods ... */ }
open
メソッドはlog_file_name
プロパティを参照し、BufferedReaderでログファイルをオープンします。この例でのログファイルはアプリケーションのディレクトリwebserverlog/WEB-INF/classes/log1.txt
に含まれています。
fileName = jobCtx.getProperties().getProperty("log_file_name"); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); InputStream iStream = classLoader.getResourceAsStream(fileName); breader = new BufferedReader(new InputStreamReader(iStream));
チェックポイントオブジェクトが存在する場合、open
メソッドは最終チェックポイントまで読み込み位置を前進させます。存在しなければ、チェックポイントオブジェクトをnewします。チェックポイントオブジェクトは最後にコミットされたチャンクの行ナンバーを記録し続けています。
read
メソッドは、newしたLogLine
を返すか、ログファイルのEOFに達したらnullを返します。
@Override public Object readItem() throws Exception { String entry = breader.readLine(); if (entry != null) { checkpoint.nextLine(); return new LogLine(entry); } else return null; }
LogLineProcessor
アーティファクトはジョブのプロパティからブラウザのリストを取得し、そのリストに従ってログエントリをフィルタします。
@Override public Object processItem(Object item) { /* Obtain a list of browsers we are interested in */ if (nbrowsers == 0) { Properties props = jobCtx.getProperties(); nbrowsers = Integer.parseInt(props.getProperty("num_browsers")); browsers = new String[nbrowsers]; for (int i = 1; i<nbrowsers+1; i++) browsers[i-1] = props.getProperty("browser_" + i); } LogLine logline = (LogLine) item; /* Filter for only the mobile/tablet browsers as specified */ for (int i=0; i<nbrowsers; i++) { if (logline.getBrowser().equals(browsers[i])) { return new LogFilteredLine(logline); } } return null; }
LogFilteredLineWriter
アーティファクトはジョブのプロパティから出力ファイル名を取得します。open
メソッドは書き込みのためにファイルをオープンします。もしチェックポイントオブジェクトが存在する場合、アーティファクトはファイルのEOFから書き込みを続行します。そうでない場合、出力ファイルが存在していれば上書きします。writeItems
メソッドはフィルタされたアイテムを出力ファイルへ書き込みます。
@Override public void writeItems(List<Object> items) throws Exception { /* Write the filtered lines to the output file */ for (int i = 0; i < items.size(); i++) { LogFilteredLine filtLine = (LogFilteredLine) items.get(i); bwriter.write(filtLine.toString()); bwriter.newLine(); } }
55.8.1.4 The Listener Batch Artifacts
InfoJobListener
バッチアーティファクトは単純なリスナで、ジョブの開始・終了ログを出力します。
@Dependent @Named("InfoJobListener") public class InfoJobListener implements JobListener { ... @Override public void beforeJob() throws Exception { logger.log(Level.INFO, "The job is starting"); } @Override public void afterJob() throws Exception { ... } }
InfoItemProcessListener
バッチアーティファクトはチャンクステップ向けのインタフェースItemProcessListener
を実装します。
@Dependent @Named("InfoItemProcessListener") public class InfoItemProcessListener implements ItemProcessListener { ... @Override public void beforeProcess(Object o) throws Exception { LogLine logline = (LogLine) o; logger.log(Level.INFO, "Processing entry " + logline); } ... }
55.8.1.5 The Task Step Batch Artifact
タスクステップの実装はMobileBatchlet
アーティファクトで、フィルタされたログエントリの何バーセントが売上になったを計算します。
@Override public String process() throws Exception { /* ... Get properties from the job definition file ... */ /* Count from the output of the previous chunk step */ breader = new BufferedReader(new FileReader(fileName)); String line = breader.readLine(); while (line != null) { String[] lineSplit = line.split(", "); if (buyPage.compareTo(lineSplit[1]) == 0) pageVisits++; totalVisits++; line = breader.readLine(); } breader.close(); /* ... Write the result ... */ }
55.8.1.6 The JavaServer Faces Pages
index.xhtml
ページはwebサーバーログを表示するテキストエリアで構成されています。このページのボタンはバッチジョブをサブミットして次のページへ遷移します。
<body> ... <textarea cols="90" rows="25" readonly="true">#{jsfBean.getInputLog()}</textarea> ... <h:form> <h:commandButton value="Start Batch Job" action="#{jsfBean.startBatchJob()}"></h:commandButton> </h:form> </body>
このページはセッションBeanのログファイルを表示するメソッドとバッチジョブをサブミットするメソッドを呼び出します。
jobstarted.xhtml
ページはバッチジョブの現在のステータスをチェックするボタンとジョブが終了したときの結果表示を行います。
<p>Current Status of the Job: <b>#{jsfBean.jobStatus}</b></p> <p>#{jsfBean.showResults()}</p> <h:form> <h:commandButton value="Check Status" action="jobstarted" rendered="#{jsfBean.completed==false}"> </h:commandButton> </h:form>
55.8.1.7 The Session Bean
JsfBean
セッションBeanはバッチランタイムへジョブをサブミットし、ジョブのステータスをチェックし、テキストファイルから結果を読み込みます。
startBatchJob
メソッドはバッチランタイムへジョブをサブミットします。
/* Submit the batch job to the batch runtime. * JSF Navigation method (return the name of the next page) */ public String startBatchJob() { jobOperator = BatchRuntime.getJobOperator(); execID = jobOperator.start("webserverlog", null); return "jobstarted"; }
getJobStatus
メソッドはジョブステータスをチェックします。
/* Get the status of the job from the batch runtime */ public String getJobStatus() { return jobOperator.getJobExecution(execID).getBatchStatus() .toString(); }
showResults
メソッドはテキストファイルから結果を読み込みます。
55.8.2 Running the webserverlog Example Application
webserverlog
サンプルアプリケーションのNetBeansとコマンドラインからの起動方法について説明します。
55.8.2.1 To Run the webserverlog Example Application Using NetBeans IDE
- Fileメニューから、Open Projectを選択する。
- Open Projectダイアログボックスでサンプルアプリケーションのディレクトリに移動する。
tut-install/examples/batch/webserverlog
webserverlog
ディレクトリを選択する。- Open Projectをクリックする。
- Projectsタブで、
webserverlog
プロジェクトを右クリックしてRunを選択する。
このコマンドは、ビルドしてwebserverlog.war
を作成し、target/
ディレクトリに配置し、サーバへデプロイし、webブラウザを立ち上げて、以下のURLを表示します。
http://localhost:8080/webserverlog/
55.8.2.2 To Run the webserverlog Example Application Using Maven
- GlassFishサーバが起動済みなことを確認してください。詳細な情報はChapter 2, "Using the Tutorial Examples"を確認してください。
- ターミナルで下記ディレクトリに移動する。
tut-install/examples/batch/webserverlog
- アプリケーションをデプロイするために下記コマンドを入力する。
mvn install
- webブラウザを起動して下記のアドレスを入力する。
http://localhost:8080/webserverlog/
55.8 The webserverlog Example Application
tut-install/examples/batch/webserverlog/
からダウンロードできるwebserverlog
サンプルアプリケーションは、Java EEバッチフレームワークでwebサーバーログを分析する方法を紹介しています。このサンプルアプリケーションはログファイルを読み込み、タブレット端末からのPVの何パーセントが売上となったか、を調査します。
55.8.1 Architecture of the webserverlog Example Application
webserverlog
サンプルアプリケーションは以下の要素から構成されます。
- ジョブ定義言語(JSL)で記述したジョブ定義ファイル(
webserverlog.xml
)にチャンクステップとタスクステップからなるバッチジョブを定義。 - バッチジョブの入力となるログファイル(
log1.txt
)。 - チャンクステップの入力・出力アイテムを表現する二つのJavaクラス(
LogLine
とLogFilteredLine
)。 - チャンクステップの実装となる三つのバッチアーティファクト(
LogLineReader
,LogLineProcessor
,LogFilteredLineWriter
)。このステップはwebサーバーログを読み込み、クライアントの使用したブラウザでフィルタし、結果をテキストファイルに書き込み、を行います。 - リスナーの単純な実装となる二つのバッチアーティファクト(
InfoJobListener
とInfoItemProcessListener
)。 - バッチアーティファクト(
MobileBatchlet.java
)はフィルタされたログを基に統計データを計算します。 - バッチアプリケーションのフロントエンドとなる二つのJSFページ(
index.xhtml
とjobstarted.xhtml
)。前者のページはバッチジョブで処理予定のログファイルを表示し、後者のページはジョブのステータスと結果を表示します。 - JSFページから使用されるセッションBean(
JsfBean
)。バッチランタイムへジョブをサブミットし、ジョブのステータスをチェックし、最後にテキストファイルから結果を読み込みます。
55.8.1.1 The Job Definition File
ジョブ定義ファイルwebserverlog.xml
はWEB-INF/classes/META-INF/batch-jobs/
ディレクトリに配置されており、七つのジョブレベルのプロパティと二つのステップを持ちます。
<?xml version="1.0" encoding="UTF-8"?> <job id="webserverlog" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0"> <properties> <property name="log_file_name" value="log1.txt"/> <property name="filtered_file_name" value="filtered1.txt"/> <property name="num_browsers" value="2"/> <property name="browser_1" value="Tablet Browser D"/> <property name="browser_2" value="Tablet Browser E"/> <property name="buy_page" value="/auth/buy.html"/> <property name="out_file_name" value="result1.txt"/> </properties> <listeners> <listener ref="InfoJobListener"/> </listeners> <step id="mobilefilter" next="mobileanalyzer"> ... </step> <step id="mobileanalyzer"> ... </step> </job>
最初のステップは以下のように定義されています。
<step id="mobilefilter" next="mobileanalyzer"> <listeners> <listener ref="InfoItemProcessListeners"/> </listeners> <chunk checkpoint-policy="item" item-count="10"> <reader ref="LogLineReader"></reader> <processor ref="LogLineProcessor"></processor> <writer ref="LogFilteredLineWriter"></writer> </chunk> </step>
このステップは通常のチャンクステップで、ステップの各フェーズを実装するバッチアーティファクトが指定されています。バッチアーティファクトの参照は完全修飾クラス名(FQDN)ではなく、@Named
アノテーションをつけられたCDI Beanで指定されています。
次のステップは以下のように定義されています。
<step id="mobileanalyzer"> <batchlet ref="MobileBatchlet"></batchlet> <end on="COMPLETED"/> </step>
ステップには、タスクステップを実装するバッチアーティファクトが指定されています。これがジョブの最終ステップです。
55.8.1.2 The LogLine and LogFilteredLine Items
LogLine
クラスはwebサーバーログファイルのエントリを表現しており、以下のように定義されています。
public class LogLine { private String datetime; private String ipaddr; private String browser; private String url; /* ... Constructor, getters, and setters ... */ }
LogFileteredLine
クラスはこのクラスと似ているが、クライアントIPアドレスとURLの二つのフィールドのみ持ちます。
55.8.1.3 The Chunk Step Batch Artifacts
最初のステップは三つのバッチアーティファクトLogLineReader
とLogLineProcessor
とLogFilteredLineWriter
で構成されています。
LogLineReader
アーティファクトはwebサーバーログを読み込みます。
@Dependent @Named("LogLineReader") public class LogLineReader implements ItemReader { private ItemNumberCheckpoint checkpoint; private String fileName; private BufferedReader breader; @Inject private JobContext jobCtx; public LogLineReader() { } /* ... Override the open, close, readItem, and * checkpointInfo methods ... */ }
open
メソッドはlog_file_name
プロパティを参照し、BufferedReaderでログファイルをオープンします。この例でのログファイルはアプリケーションのディレクトリwebserverlog/WEB-INF/classes/log1.txt
に含まれています。
fileName = jobCtx.getProperties().getProperty("log_file_name"); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); InputStream iStream = classLoader.getResourceAsStream(fileName); breader = new BufferedReader(new InputStreamReader(iStream));
チェックポイントオブジェクトが存在する場合、open
メソッドは最終チェックポイントまで読み込み位置を前進させます。存在しなければ、チェックポイントオブジェクトをnewします。チェックポイントオブジェクトは最後にコミットされたチャンクの行ナンバーを記録し続けています。
read
メソッドは、newしたLogLine
を返すか、ログファイルのEOFに達したらnullを返します。
@Override public Object readItem() throws Exception { String entry = breader.readLine(); if (entry != null) { checkpoint.nextLine(); return new LogLine(entry); } else return null; }
LogLineProcessor
アーティファクトはジョブのプロパティからブラウザのリストを取得し、そのリストに従ってログエントリをフィルタします。
@Override public Object processItem(Object item) { /* Obtain a list of browsers we are interested in */ if (nbrowsers == 0) { Properties props = jobCtx.getProperties(); nbrowsers = Integer.parseInt(props.getProperty("num_browsers")); browsers = new String[nbrowsers]; for (int i = 1; i<nbrowsers+1; i++) browsers[i-1] = props.getProperty("browser_" + i); } LogLine logline = (LogLine) item; /* Filter for only the mobile/tablet browsers as specified */ for (int i=0; i<nbrowsers; i++) { if (logline.getBrowser().equals(browsers[i])) { return new LogFilteredLine(logline); } } return null; }
LogFilteredLineWriter
アーティファクトはジョブのプロパティから出力ファイル名を取得します。open
メソッドは書き込みのためにファイルをオープンします。もしチェックポイントオブジェクトが存在する場合、アーティファクトはファイルのEOFから書き込みを続行します。そうでない場合、出力ファイルが存在していれば上書きします。writeItems
メソッドはフィルタされたアイテムを出力ファイルへ書き込みます。
@Override public void writeItems(List<Object> items) throws Exception { /* Write the filtered lines to the output file */ for (int i = 0; i < items.size(); i++) { LogFilteredLine filtLine = (LogFilteredLine) items.get(i); bwriter.write(filtLine.toString()); bwriter.newLine(); } }
55.8.1.4 The Listener Batch Artifacts
InfoJobListener
バッチアーティファクトは単純なリスナで、ジョブの開始・終了ログを出力します。
@Dependent @Named("InfoJobListener") public class InfoJobListener implements JobListener { ... @Override public void beforeJob() throws Exception { logger.log(Level.INFO, "The job is starting"); } @Override public void afterJob() throws Exception { ... } }
InfoItemProcessListener
バッチアーティファクトはチャンクステップ向けのインタフェースItemProcessListener
を実装します。
@Dependent @Named("InfoItemProcessListener") public class InfoItemProcessListener implements ItemProcessListener { ... @Override public void beforeProcess(Object o) throws Exception { LogLine logline = (LogLine) o; logger.log(Level.INFO, "Processing entry " + logline); } ... }
55.8.1.5 The Task Step Batch Artifact
タスクステップの実装はMobileBatchlet
アーティファクトで、フィルタされたログエントリの何バーセントが売上になったを計算します。
@Override public String process() throws Exception { /* ... Get properties from the job definition file ... */ /* Count from the output of the previous chunk step */ breader = new BufferedReader(new FileReader(fileName)); String line = breader.readLine(); while (line != null) { String[] lineSplit = line.split(", "); if (buyPage.compareTo(lineSplit[1]) == 0) pageVisits++; totalVisits++; line = breader.readLine(); } breader.close(); /* ... Write the result ... */ }
55.8.1.6 The JavaServer Faces Pages
index.xhtml
ページはwebサーバーログを表示するテキストエリアで構成されています。このページのボタンはバッチジョブをサブミットして次のページへ遷移します。
<body> ... <textarea cols="90" rows="25" readonly="true">#{jsfBean.getInputLog()}</textarea> ... <h:form> <h:commandButton value="Start Batch Job" action="#{jsfBean.startBatchJob()}"></h:commandButton> </h:form> </body>
このページはセッションBeanのログファイルを表示するメソッドとバッチジョブをサブミットするメソッドを呼び出します。
jobstarted.xhtml
ページはバッチジョブの現在のステータスをチェックするボタンとジョブが終了したときの結果表示を行います。
<p>Current Status of the Job: <b>#{jsfBean.jobStatus}</b></p> <p>#{jsfBean.showResults()}</p> <h:form> <h:commandButton value="Check Status" action="jobstarted" rendered="#{jsfBean.completed==false}"> </h:commandButton> </h:form>
55.8.1.7 The Session Bean
JsfBean
セッションBeanはバッチランタイムへジョブをサブミットし、ジョブのステータスをチェックし、テキストファイルから結果を読み込みます。
startBatchJob
メソッドはバッチランタイムへジョブをサブミットします。
/* Submit the batch job to the batch runtime. * JSF Navigation method (return the name of the next page) */ public String startBatchJob() { jobOperator = BatchRuntime.getJobOperator(); execID = jobOperator.start("webserverlog", null); return "jobstarted"; }
getJobStatus
メソッドはジョブステータスをチェックします。
/* Get the status of the job from the batch runtime */ public String getJobStatus() { return jobOperator.getJobExecution(execID).getBatchStatus() .toString(); }
showResults
メソッドはテキストファイルから結果を読み込みます。
55.8.2 Running the webserverlog Example Application
webserverlog
サンプルアプリケーションのNetBeansとコマンドラインからの起動方法について説明します。
55.8.2.1 To Run the webserverlog Example Application Using NetBeans IDE
- Fileメニューから、Open Projectを選択する。
- Open Projectダイアログボックスでサンプルアプリケーションのディレクトリに移動する。
tut-install/examples/batch
webserverlog
ディレクトリを選択する。- Open Projectをクリックする。
- Projectsタブで、
webserverlog
プロジェクトを右クリックしてRunを選択する。
このコマンドは、ビルドしてwebserverlog.war
を作成し、target/
ディレクトリに配置し、サーバへデプロイし、webブラウザを立ち上げて、以下のURLを表示します。
http://localhost:8080/webserverlog/
55.8.2.2 To Run the webserverlog Example Application Using Maven
- GlassFishサーバが起動済みなことを確認してください。詳細な情報はChapter 2, "Using the Tutorial Examples"を確認してください。
- ターミナルで下記ディレクトリに移動する。
tut-install/examples/batch/webserverlog
- アプリケーションをデプロイするために下記コマンドを入力する。
mvn install
- webブラウザを起動して下記のアドレスを入力する。
http://localhost:8080/webserverlog/
55.9 The phonebilling Example Application
tut-install/examples/batch/phonebilling/
からダウンロードできるphonebilling
サンプルアプリケーションは、Java EEバッチフレームワークで通話請求システムを実装する方法を紹介しています。このサンプルアプリケーションは通話ログファイルを処理して顧客ごとに請求を作成します。
55.9.1 Architecture of the phonebilling Example Application
phonebilling
サンプルアプリケーションは以下の要素から構成されます。
- ジョブ定義言語(JSL)で記述したジョブ定義ファイル(
phonebilling.xml
)に二つのチャンクステップからなるバッチジョブを定義。最初のステップはログファイルから通話記録を読み込んで請求と関連付けます。次のステップは請求金額を計算してテキストファイルに個々の請求を書き込みます。 - バッチジョブのためにログファイルを生成するJavaクラス(
CallRecordLogCreator
)。これは補足的なコンポーネントで、この例における主要機能として紹介はされません。 - 通話記録と請求を表現する二つのJPAエンティティ(
CallRecord
とPhoneBill
)。アプリケーションはデータベースへこれらのエンティティを保存するためにJPAのEntity Managerを使用します。 - 二番目のステップを実装する四つのバッチアーティファクト(
BillReader
,BillProcessor
,BillWriter
,BillPartitionMapper
)。このステップはパーティーションステップで、データベースから請求を取得し、請求金額を計算し、テキストファイルへ書き込みます。 - バッチアプリケーションのフロントエンドとなる二つのJSFページ(
index.xhtml
とjobstarted.xhtmll
)。前者のページはバッチジョブで処理予定のログファイルを表示し、後者のページはジョブのステータスと顧客ごとの請求を表示します。 - JSFページから使用されるセッションBean(
JsfBean
)。バッチランタイムへジョブをサブミットし、ジョブのステータスをチェックし、最後に請求のテキストファイルを読み込みます。
55.9.1.1 The Job Definition File
ジョブ定義ファイルphonebilling.xml
はWEB-INF/classes/META-INF/batch-jobs/
ディレクトリに配置されており、三つのジョブレベルのプロパティと二つのステップを持ちます。
<?xml version="1.0" encoding="UTF-8"?> <job id="phonebilling" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0"> <properties> <property name="log_file_name" value="log1.txt"/> <property name="airtime_price" value="0.08"/> <property name="tax_rate" value="0.07"/> </properties> <step id="callrecords" next="bills"> ... </step> <step id="bills"> ... </step> </job>
最初のステップは以下にように定義されています。
<step id="callrecords" next="bills"> <chunk checkpoint-policy="item" item-count="10"> <reader ref="CallRecordReader"></reader> <processor ref="CallRecordProcessor"></processor> <writer ref="CallRecordWriter"></writer> </chunk> </step>
このステップは通常のチャンクステップで、ステップの各フェーズを実装するバッチアーティファクトが指定されています。バッチアーティファクトの参照は完全修飾クラス名(FQDN)ではなく、@Named
アノテーションをつけられたCDI Beanで指定されています。
次のステップは以下のように定義されています。
<step id="bills"> <chunk checkpoint-policy="item" item-count="2"> <reader ref="BillReader"></reader> <processor ref="BillProcessor"></processor> <writer ref="BillWriter"></writer> </chunk> <partition> <mapper ref="BillPartitionMapper"/> </partition> <end on="COMPLETED"/> </step>
このステップはパーティーションチャンクステップです。パーティーションプランはplan
要素を使用する代わりにBillPartitionMapper
アーティファクトで指定されています。
55.9.1.2 The CallRecord and PhoneBill Entities
CallRecord
エンティティは以下のように定義されています。
@Entity public class CallRecord implements Serializable { @Id @GeneratedValue private Long id; @Temporal(TemporalType.DATE) private Date datetime; private String fromNumber; private String toNumber; private int minutes; private int seconds; private BigDecimal price; public CallRecord() { } public CallRecord(String datetime, String from, String to, int min, int sec) throws ParseException { ... } public CallRecord(String jsonData) throws ParseException { ... } /* ... Getters and setters ... */ }
id
フィールドはデータベースからCallRecord
オブジェクトの取得および保存のためにJPAによって自動的に生成されます。
二番目のコンストラクタはJSON Processing APIを使用してログファイルのJSONからCallRecord
オブジェクトを生成します。ログファイルのエントリは以下のようになっています。
{"datetime":"03/01/2013 04:03","from":"555-0101", "to":"555-0114","length":"03:39"}
PhoneBill
エンティティは以下のように定義されています。
@Entity public class PhoneBill implements Serializable { @Id private String phoneNumber; @OneToMany(cascade = CascadeType.PERSIST) @OrderBy("datetime ASC") private List<CallRecord> calls; private BigDecimal amountBase; private BigDecimal taxRate; private BigDecimal tax; private BigDecimal amountTotal; public PhoneBill() { } public PhoneBill(String number) { this.phoneNumber = number; calls = new ArrayList<>(); } public void addCall(CallRecord call) { calls.add(call); } public void calculate(BigDecimal taxRate) { ... } /* ... Getters and setters ... * }
OneToMany
アノテーションは請求と通話履歴のリレーションを定義しています。CascadeType.PERSIST
パラメーターは、PhoneBillがパーシストされたときにListOrderBy
アノテーションはデータベースからList
バッチアーティファクトはこれら二つのエンティティを、読み込み・処理・書き込みに使用します。
JPAの詳細については、Chapter 37, "Introduction to the Java Persistence API"を、JSON Processing APIについては、Chapter 19, "JSON Processing"を参照してください。
55.9.1.3 The Call Records Chunk Step
最初のステップはCallRecordReader
, CallRecordProcessor
, CallRecordWriter
のバッチアーティファクトで構成されています。
CallRecordReader
アーティファクトはログファイルから通話記録を読み込みます。
@Dependent @Named("CallRecordReader") public class CallRecordReader implements ItemReader { private ItemNumberCheckpoint checkpoint; private String fileName; private BufferedReader breader; @Inject JobContext jobCtx; /* ... Override the open, close, readItem, * and checkpointInfo methods ... */ }
open
メソッドはlog_filename
プロパティをプロパティを参照し、BufferedReaderでログファイルをオープンします。
fileName = jobCtx.getProperties().getProperty("log_file_name"); breader = new BufferedReader(new FileReader(fileName));
チェックポイントオブジェクトが存在する場合、open
メソッドは最終チェックポイントまで読み込み位置を前進させます。存在しなければ、チェックポイントオブジェクトをnewします。チェックポイントオブジェクトは最後にコミットされたチャンクの行ナンバーを記録し続けています。
read
メソッドは、newしたCallRecord
を返すか、ログファイルのEOFに達したらnullを返します。
@Override public Object readItem() throws Exception { /* Read a line from the log file and * create a CallRecord from JSON */ String callEntryJson = breader.readLine(); if (callEntryJson != null) { checkpoint.nextItem(); return new CallRecord(callEntryJson); } else return null; }
CallRecordProcessor
アーティファクトはジョブのプロパティから通話時間当たりの料金を取得し、通話ごとの料金を計算します。このアーティファクトはprocessItem
メソッドのみオーバーライドします。
CallRecordWriter
アーティファクトは通話記録と請求を関連付けてデータベースへ保存します。このアーティファクトは、open
, close
, writeItems
, checkpointInfo
メソッドをオーバーライドします。writeItems
メソッドは以下のようになっています。
@Override public void writeItems(List<Object> callList) throws Exception { for (Object callObject : callList) { CallRecord call = (CallRecord) callObject; PhoneBill bill = em.find(PhoneBill.class, call.getFromNumber()); if (bill == null) { /* No bill for this customer yet, create one */ bill = new PhoneBill(call.getFromNumber()); bill.addCall(call); em.persist(bill); } else { /* Add call to existing bill */ bill.addCall(call); } } }
55.9.1.4 The Phone Billing Chunk Step
次のステップは、BillReader
, BillProcessor
, BillWriter
, BillPartitionMapper
バッチアーティファクトから構成されています。このステップはデータベースから請求を取得し、税金と合計金額を計算し、テキストファイルへ請求を書き込みます。個々の請求に対する処理は他の請求とは独立しているので、このステップはパーティションにして1つ以上のスレッドで動作させられます。
BillPartitionMapper
アーティファクトはパーティーション数とパーティションごとのパラメータを指定します。この例では、パラメータは各パーティションが処理すべきアイテムの処理範囲になっています。このアーティファクトは与えられた処理範囲を計算するためにデータベースから請求を取得します。また、PartitionPlan
インタフェースのgetPartitions
とgetPartitionProperties
をオーバーライドしたパーティーションのプランオブジェクトの役割も提供します。getPartitions
メソッドは以下のようになります。
@Override public Properties[] getPartitionProperties() { /* Assign an (approximately) equal number of elements * to each partition. */ long totalItems = getBillCount(); long partItems = (long) totalItems / getPartitions(); long remItems = totalItems % getPartitions(); /* Populate a Properties array. Each Properties element * in the array corresponds to a partition. */ Properties[] props = new Properties[getPartitions()]; for (int i = 0; i < getPartitions(); i++) { props[i] = new Properties(); props[i].put("firstItem", i * partItems); /* Last partition gets the remainder elements */ if (i == getPartitions() - 1) { props[i].put("numItems", partItems + remItems); } else { props[i].put("numItems", partItems); } } return props; }
BillReader
アーティファクトはパーティーションのパラメータを以下のように取得します。
@Dependent @Named("BillReader") public class BillReader implements ItemReader { ... @Inject JobContext jobCtx; private Properties partParams; ... @Override public void open(Serializable ckpt) throws Exception { /* Get the parameters for this partition */ JobOperator jobOperator = BatchRuntime.getJobOperator(); long execID = jobCtx.getExecutionId(); partParams = jobOperator.getParameters(execID); /* Get the range of items to work on in this partition */ long firstItem0 = ((Long) partParams.get("firstItem")).longValue(); long numItems0 = ((Long) partParams.get("numItems")).longValue(); ... } ... }
このアーティファクトはまたJPAエンティティマネージャからデータを読み込むためのイテレータも取得します。
/* Obtain an iterator for the bills in this partition */ String query = "SELECT b FROM PhoneBill b ORDER BY b.phoneNumber"; Query q = em.createQuery(query) .setFirstResult((int)firstItem).setMaxResults((int)numItems); iterator = q.getResultList().iterator();
BillProcessor
アーティファクトは通話記録のリストを走査し、請求ごとに税金と合計金額を計算します。
BillWriter
アーティファクトはテキストファイルに請求を書き込みます。
55.9.1.5 The JavaServer Faces Pages
index.xhtml
は通話記録のログファイルを表示するテキストエリアを持ちます。このページはバッチジョブとサブミットして次のページへ遷移するためのボタンを提供します。
<body> <h1>The Phone Billing Example Application</h1> <h2>Log file</h2> <p>The batch job analyzes the following log file:</p> <textarea cols="90" rows="25" readonly="true">#{jsfBean.createAndShowLog()}</textarea> <p> </p> <h:form> <h:commandButton value="Start Batch Job" action="#{jsfBean.startBatchJob()}" /> </h:form> </body>
このページは、ログファイルの表示とバッチジョブをサブミットするためにマネージドBeanのメソッドを呼び出します。
jobstarted.xhtml
はバッチジョブの現在のステータスをチェックするためのボタンとジョブが終了したときには請求を表示します。
<p>Current Status of the Job: <b>#{jsfBean.jobStatus}</b></p> <h:dataTable var="_row" value="#{jsfBean.rowList}" border="1" rendered="#{jsfBean.completed}"> <!-- ... show results from jsfBean.rowList ... --> </h:dataTable> <!-- Render the check status button if the job has not finished --> <h:form> <h:commandButton value="Check Status" rendered="#{jsfBean.completed==false}" action="jobstarted" /> </h:form>
55.9.1.6 The Managed Bean
JsfBean
マネージドBeanはバッチランタイムへジョブをサブミットし、ジョブのステータスをチェックし、請求ごとのテキストファイルを読み込みます。
startBatchJob
メソッドはバッチランタイムへジョブをサブミットします。
/* Submit the batch job to the batch runtime. * JSF Navigation method (return the name of the next page) */ public String startBatchJob() { jobOperator = BatchRuntime.getJobOperator(); execID = jobOperator.start("phonebilling", null); return "jobstarted"; }
getJobStatus
メソッドはジョブのステータスをチェックします。
/* Get the status of the job from the batch runtime */ public String getJobStatus() { return jobOperator.getJobExecution(execID).getBatchStatus().toString(); }
getRowList
メソッドはjobstarted.xhtml
で表示するために請求のリストを生成します。
55.9.2 Running the phonebilling Example Application
NetBeans IDEやMavenでビルド・パッケージ・デプロイしてphonebilling
サンプルアプリケーションを起動することが出来ます。
55.9.2.1 To Run the phonebilling Example Application Using NetBeans IDE
- GlassFishサーバが起動済みなことを確認してください。詳細な情報はChapter 2, "Using the Tutorial Examples"を確認してください。
- Fileメニューから、Open Projectを選択する。
- Open Projectダイアログボックスでサンプルアプリケーションのディレクトリに移動する。
tut-install/examples/batch
phonebilling
ディレクトリを選択する。- Open Projectをクリックする。
- Projectsタブで、
phonebilling
プロジェクトを右クリックしてRunを選択する。
このコマンドは、ビルドしてphonebilling.war
を作成し、target/
ディレクトリに配置し、サーバへデプロイし、webブラウザを立ち上げて、以下のURLを表示します。
http://localhost:8080/phonebilling/
55.8.2.2 To Run the webserverlog Example Application Using Maven
- GlassFishサーバが起動済みなことを確認してください。詳細な情報はChapter 2, "Using the Tutorial Examples"を確認してください。
- ターミナルで下記ディレクトリに移動する。
tut-install/examples/batch/phonebilling/
- アプリケーションをデプロイするために下記コマンドを入力する。
mvn install
- webブラウザを起動して下記のアドレスを入力する。
http://localhost:8080/phonebilling/
55.10 Further Information about Batch Processing
Java EEのバッチ処理に関するより詳しい情報は、Batch Applications for the Java Platform specificationを参照してください。
http://www.jcp.org/en/jsr/detail?id=352
参考文献
- The Java EE 7 Tutorial:Batch Processing | Java EE Documentation
- jBatch のお試し実装 - Qiita
- Batch Applications for the Java Platform (JSR-352) Java EE 7 New Fe...
- Java Batch 仕様 (Public Review時点)
- JSR 352 “Batch Applications for the Java Platform”
- 英語・語学の学習情報サイト「スペースアルク」:アルク
- Google 翻訳
関連エントリ
- The Java EE 7 Tutorialのテキトー翻訳まとめ - Qiita - Java EE 7 Tutorialのうち、自分がテキトー翻訳したものの一覧
- JSR 352 1.0 Final ReleaseいわゆるjBatchの仕様をテキトーに翻訳した - kagamihogeの日記
- jBatch(JSR-352) on GlassFish 4.0でhello world的なことやる - kagamihogeの日記
- jBatch(JSR-352) on Java SEでhello world的なことやる - kagamihogeの日記
*1:原文では、下記の一覧からそれぞれの節のページへとリンクが張られているが、面倒なので省略している
*2:割と意味不明だけどcreates a bill if one does not exist for an accountがうまく訳せない……
*3:decision elementsのみ条件分岐と訳し、その他の要素のチャンク(chunk)とかステップ(step)とかはそのままカタカナにしている。固有名詞はそのまんまじゃないと逆に意図が伝わりにくいと思うんで。ただし『デシジョンエレメント』はさすがに座りが悪かったんで、コレだけは適当な日本語を当てることにした
*4:ここに限らずitemはそのまんまアイテムにしている。若干不自然だけど、まぁ意味は通じるし……
*5:a single call to the item writerって「ライターが一回だけ呼び出される」で良いのだろうか?
*6:原文はbatchとなっているが、batckletのミスタイプと思われる
*7:do not require separate packagingってこういう意味でいいのか?
*8:おそらく https://java.net/projects/javaeetutorial/sources/svn/show/trunk/examples/batch と思われる。