作ってみようJavaバッチ ~ WebSphere Application Serverのバッチ機能を使う

この記事では、WebSphere Application Server(WAS)が提供するJavaバッチ機能を、簡単なサンプルの開発を通じて紹介します。最初に、WASが提供するJavaバッチ機能の概要を述べた後、実際にバッチ・アプリケーションのサンプルを作成して、WASで実行するまでの各ステップを解説していきます。これらのステップを通じて、バッチ・ジョブ・ステップやバッチ・データ・ストリーム(BDS)といったプログラミング・モデル、開発効率を向上するBDSフレームワーク、及び、ジョブを記述するxJCL、といった概念を理解します。最後に、より高機能なJavaバッチ基盤を提供するWebSphere Extended Deployment Compute Gridの概要、及び、Rational Application Developer V8が提供するJavaバッチ開発機能を簡単に紹介します。

はじめに

6月17日にWebSphere Application Server(WAS)の最新メイン・バージョンV8の提供が始まりました。Java EE 6準拠を始め、多くの機能強化が図られていますが、その中でもユニークなのが、Javaバッチ機能の標準搭載です。バッチは、これまでJavaの世界では、オンラインに比して、あまり焦点が当って来なかったため、ピンと来ない方も多いかもしれません。しかし、企業システムでは、全ての処理がオンラインだけで完結するわけでは無く、時間を要する処理や、まとめて行った方が効率が高い処理など、基幹業務処理の多くの場面でバッチが重要な役割を果たしています。一方、その重要性や歴史的経緯から、バッチ処理はCOBOLやPL/Iといった言語で実装され、メインフレーム上で稼動させているケースが多くなっています。今回のWASの新機能により、Javaで作成されたバッチが、Java EEアプリケーション・サーバー上で稼動することになります。これによって、オンライン系のアプリケーションとのビジネス・ロジックとの共有や連携、開発や運用のスキルの共通化などを図ることができます。

当記事では、簡単なサンプルの作成を通して、実際にWASのJavaバッチの開発での主要概念を解説します。入出力を伴わないサンプルから始めて、ファイル入力処理を追加し、最後に、ファイル出力処理を加えていきます。後半の入出力の開発では、製品が提供するフレームワークを利用します。このフレームワークによって、開発の効率が如何に大きく向上するかをご覧ください。読者の皆さんには、WASを入手して、是非このJavaバッチ機能を試してみて欲しいと思います。(参考文献に無料ダウンロードのURLを掲載してあります)


1.WebSphere Application ServerのJavaバッチ機能の概要

この章では、WebSphere Application Server V8に搭載されたJavaバッチ機能の概要を紹介します。

長時間実行ジョブのための基盤機能(グリッド・スケジューラとバッチ・コンテナ)
オンライン処理に対してバッチ処理の特徴は、実行に時間を要することです。実行の単位はジョブと呼ばれますが、WASのJavaバッチ機能は、このような長時間実行ジョブのための基盤を提供します。具体的には、ジョブの実行要求(サブミット)を受付てキューイングし、適切なタイミングで実行を指示(ディスパッチ)するグリッド・スケジューラと、指示を受けて実際のジョブの実行をホストするバッチ・コンテナから成ります。(バッチ・コンテナが稼動するサーバー・インスタンスは、グリッド・エンド・ポイント、GEEと呼ばれます)クラスター構成であれば、スケジューラは、稼動しているサーバーを認識して、複数のジョブをそれぞれ別のサーバー(GEE)に割振って並列処理することができます。ユーザーは、個々のサーバーを意識することなく、ジョブのサブミット、停止、再スタート、ジョブ・ログの取得といった操作やジョブ状況の監視をグリッド・スケジューラを通じて一元的に行うことができます。これら一連の操作は、コマンド・ラインやWeb UI(ジョブ管理コンソール)で行えますし、EJBやWebサービスのインターフェースを使用して、プログラムから行うことも可能です。(図1)

図1. WAS Javaバッチ基盤の概要 (ND版 クラスタ構成の場合)
図1. WAS Javaバッチ基盤の概要 (ND版 クラスタ構成の場合)

ジョブ記述のためのxJCL
バッチ処理では、再利用性・保守容易性を高めるため、処理をより小さい機能単位に分割し、これらを組み合わせて一塊の処理を行うことが良くあります。例えば、最初の処理で、DBからファイルを抽出し、次の処理で、このファイルをソートし、更に次の処理がこのファイルを入力に処理を行い出力ファイルを生成、最後にこの出力ファイルの内容を元にDBを更新する処理という様に、個々の処理を組み合わせて1つのジョブとします。WASでは、こういった1連の処理をジョブとして記述するために、xJCL(JCLは、元はJob Control Languageの頭文字)を使用します。xJCLはXMLファイルで、組合わせる単体の各処理(ジョブ・ステップと呼びます)で使用するJavaクラスの指定のほか、 チェック・ポイント(後述)のタイミング設定や、パラメータの受け渡し、条件付実行等を記述できます。xJCLによって、Javaプログラムを変更することなく、状況に応じて組み合わせや設定を選択してジョブを実行することができます。

復旧時間を短縮するチェック・ポイント&リスタート機能
バッチ処理を行うためにオンライン処理を停止する時間帯、いわゆるバッチ・ウィンドウを設けている場合、途中で障害が発生した時に処理を最初からやり直していては、バッチ・ウィンドウを突き抜けて、オンラインの再開時刻までにバッチ処理が完了しない懸念があります。WASのJavaバッチ機能では、障害時の処理の復旧に要する時間を短縮するための、チェック・ポイント&リスタート機能を提供しています。バッチ実行基盤は指定されたタイミングでバッチの状態データをDBに保存し、もし、障害による中断が発生すれば、障害発生直前のチェック・ポイント時の状態を復旧し、その時点から処理を再開します。

容易な開発を支援するフレームワーク
WASのJavaバッチ機能では、チェック・ポイント&リスタート機能やxJCLパラメータの取得などのために、バッチ・コンテナとのインターフェースが定義されています。このインターフェースを使うことで、開発者はバッチ基盤機能との連携を図りながら、様々なケースに対応できます。一方、処理や入出力には多くの場合に適合する一定のパターンがあります。例えば、1つのテキスト・ファイルから入力して処理を行い、結果を1つのテキスト・ファイルに出力するといった処理は、非常に一般的に行われる処理です。WASでは、このような典型的なパターンに適合する場合に、インターフェースの実装を省き、開発を省力化するためのフレームワークを提供しています。

以上、WASが提供する本格的なJavaバッチ基盤について、その機能の概要を紹介しました。


2.WebSphere Application Server バッチ実行環境のセットアップ

この章では、Javaバッチを実行するためのWAS環境の概要とその構築手順を説明します。

Javaバッチ実行環境を構成する製品
WAS V8が搭載したJavaバッチ機能は、元来WASのアドオン製品であるWebSphere Extended Deployment Compute Grid(WCG)が提供してきた機能のコア部分になります。また、同機能は、昨年リリースされたWAS V7に対する無償のフィーチャーFeature Pack for Modern Batchにも含まれています。したがって、WASのJavaバッチ実行環境を構成するには、次の3つの製品パターンがあります。

① WAS V8
WAS V8には、Javaバッチ機能が標準搭載されています。クラスター構成には、Network Deployment(ND)版が必要です。他の版では、スケジューラとコンテナが同じサーバー・インスタンス上で動作するシングル・サーバー構成のみが可能です。

② WAS V7+ Feature Pack for Modern Batch
WAS V7でも、Feature Pack for Modern Batch を導入することで、Javaバッチ機能を利用できるようになります(バッチの機能としてWAS V8とほぼ同等です)。V8と同様に、クラスター構成には、ベースとなるWASがND版である必要があります。

③ WAS V7/V8 ND + WebSphere Extended Deployment Compute Grid (WCG)
別製品である WCGを導入することで、V8やV7に比べて、より高度なバッチ機能を利用することができます。前提となる、WASのエディションはND版になります。6月18日に、新規バージョンV8.0がリリースされています。

当記事では、①或いは②の構成を前提に記述しています。(両構成で違いがある場合には、明記します)この記事では③の構成取り上げませんが、コア機能の基本的な部分は①②と相違ありません。③だけが備える機能については、最後の章で概要を紹介します。

Javaバッチ実行環境のセットアップ
まず、上で紹介した3つのパターンのいずれかの製品を導入します。(製品の導入方法に関しては、それぞれの製品のドキュメントを参照してください)この記事では、無償のWAS for Developers (V7/V8)の利用を想定しています。(V7の場合は、更にFeP for Modern Batch を導入します。ダウンロード・サイトのURLについては、参考資料を参照ください)
製品の導入が完了したら、アプリケーション・サーバー用のプロファイルを作成します。V7+ FeP for Modern Batchの場合は、さらに、作成したプロファイルの拡張を行います([補足情報] を参照。

[補足情報]Feature Pack for Modern Batchによるプロファイル拡張の手順

プロファイル管理ツールを起動して、対象プロファイルを選択して、「拡張(A)」ボタンをクリックし、ポップアップ・ウィンドウで「WebSphere Application Server V7 Feature Pack for Modern Batchのアプリケーション・サーバー」を選んで、「次へ」をクリックします。必要なディスク容量等を表示した確認画面が表示されるので、「拡張」ボタンをクリックすれば完了です。プロファイルの一覧画面で、+をクリックして展開すれば、拡張されていることが確認できます。確認できたら、プロファイル管理ツールを終了します。

プロファイル作成(及び拡張)が完了したら、サーバーを起動します。起動後に、管理コンソールを開いて、スケジューラのセットアップを行います。「システム管理」の「ジョブ・スケジューラー」を選ぶと、スケジューラのセットアップ画面が表示されます。ここでは、デフォルトの設定を、そのまま受け入れるので「スケジューラのホスト」のリスト(シングル・サーバー環境では1エントリーのみ)からホストを選択し(図2)、OKをクリックします。変更完了後、保存します。

*注意: デフォルトのジョブ管理用DBは、ローカルのDerby DBになります。今回は開発環境なので、デフォルトのDerby DBとしましたが、商用環境では、データベース製品の使用が推奨されます。その場合は、データソースを予めセルに対して設定しておく必要があります。

図2. ジョブ・スケジューラの構成
図2. ジョブ・スケジューラの構成

保存が完了したら、サーバーを再起動します。再起動後、次のURLにアクセスして、ジョブ管理コンソールへアクセスできることを確認します。(図3参照)ここで、ポート番号は、管理コンソールで対象アプリケーション・サーバーを選択し、「通信」-「ポート」で、WC_defaulthostのものを使用します。

http://<ホスト名>:<ポート番号>/jmc

図3. ジョブ管理コンソール
図3. ジョブ管理コンソール

3.Javaバッチの開発:プログラミング・モデルと開発ステップ

Javaバッチの稼働環境のセットアップが完了したので、次は、いよいよJavaバッチの開発に取りかかります。

コンパイル&パッケージングのための設定
お使いのJava開発環境が使用できます。追加で必要なセットアップは、WAS導入先ディレクトリ下のlibにあるbatfepapi.jarへのクラスパスの設定だけです。以下は、Windowsでの設定の例です。

リスト1. 環境設定例
set WAS_HOME=C:\WebSphere\AppServer
set CLASSPATH=%CLASSPAH%;%WAS_HOME%\lib\batfepapi.jar

開発ステップ
主な開発のステップは、以下の様になります。

① プログラム開発
Javaバッチのプログラムを作成します。詳細は、次節以降で説明しますが、作成するのは、ジョブのレコード処理内容を記述するプログラム(バッチ・ジョブ・ステップ)、及び、レコードの入出力を担うプログラム(バッチ・データ・ストリーム)になります。

② パッケージング
作成したプログラムをコンパイルした後、専用のパッケージング・ツールを使って、earにパッケージングします。

③ デプロイ
パッケージングで生成したearをWASアプリケーション・サーバーにデプロイします。

④ xJCLの作成
デプロイしたJavaバッチ・アプリケーションを実行すために、バッチ・ジョブの内容をxJCLに記述します。

⑤ ジョブのサブミット
作成したxJCLをジョブ管理コンソールを通じてサブミットします。

⑥ 実行結果の確認
ジョブ管理コンソールのジョブ一覧でジョブの状況が確認できます。状態が完了となったら、ジョブ・ログの内容をチェックして、正常完了したかを確認します。

プログラミング・モデル
開発に先立ってJavaバッチのプログラミング・モデルを理解しておきましょう。Javaバッチのプログラミング・モデルには2つの型がありますが、この記事では、そのうちチェック・ポイント&リスタート機能をサポートするトランザクショナル・バッチ型のみを扱うことにします。(もう1つは、シミュレーション計算などを想定した計算集約型になります。詳細は、参考資料にあるWASのインフォセンターを参照してください)
トランザクショナル・バッチ型のプログラミング・モデルでは、2種類のコンポーネントが連携して動作します。1つは、レコードの入出力を担うバッチ・データ・ストリーム(略してBDS)、もう1つは、レコードをどう処理するかを記述するバッチ・ジョブ・ステップです。それぞれ、バッチ・コンテナとのインターフェースとなる幾つかのメソッドを実装する必要があります。図4は、入力用のBDSと出力用のBDSが1つずつといった場合の、プログラミング・モデルを図示したものです。(実際には、BDSは、1つ以上幾つでも使用できます)

図4. トランザクショナル・バッチ型のプログラミング・モデル(1入力・1出力の場合)
図4. トランザクショナル・バッチ型のプログラミング・モデル(1入力・1出力の場合)

サンプル・プログラムの処理内容
さて、最初のサンプル・プログラムを作成しましょう。処理内容は、レコードとして名前を読込み、その先頭に「Hello 」、末尾に「!」をつけて出力するといった簡単なものです。

入力用バッチ・データ・ストリーム(BDS)の作成
まず、名前を入力するバッチ・データ・ストリームを作成します。本来ならファイルやDBから読み込むところですが、最初のステップでは簡単のため、プログラム中の変数で定義しました。バッチ・ジョブ・ステップには、getData()メソッドで、1レコードずつ渡す仕様です。

リスト2. BDSのサンプルコード(HelloBatchBDS)
(省略)
public class HelloBatchBDS implements BatchDataStream {

  private String bdsJobStepID;
  private String bdsLogicalName;

  private Properties props = null;
  private int position;
  private int loopMax=10;
  private static final String[] data 
= {"Wan","Usagi","Wani","Lion","Unagi","Mouse","Sukanku","Manbo","Sai"};

  /**
   *  初期化
   *  logicalName: BDSの論理名
   *  jobStepid: ジョブステップID
   */
  public void initialize(String logicalName, String jobStepID )
     throws BatchContainerDataStreamException {
        this.bdsLogicalName = logicalName;
        this.bdsJobStepID = jobStepID;
   }

  /* チェックポイント・データの初期化 */
  public void positionAtInitialCheckpoint()
			throws BatchContainerDataStreamException {
        this.position=0;
   }

  /* ファイルやDB接続のオープン */
   public void open() throws BatchContainerDataStreamException {}
   public void close() throws BatchContainerDataStreamException {}
   
  /* チェック・ポイントの出力 */
   public String externalizeCheckpointInformation() {
		return Integer.toString(this.position);
	}

  /*トランザクションが完了し、チェック・ポイントを保存した直後の処理 */
   public void intermediateCheckpoint() {}

  /* チェック・ポイント・データを受け取り、セット */
   public void internalizeCheckpointInformation(String posStr) {
      position = Integer.parseInt(posStr);
   }

  /*  セットされたチェック・ポイントを元に、状態を復旧  */
   public void positionAtCurrentCheckpoint()
		throws BatchContainerDataStreamException {}

  /* プロパティのセット*/
   public void setProperties(Properties props) {
        this.props = props;
   }

  /* プロパティのゲット */
   public Properties getProperties() {
       return this.props;
   }

  /* BDSの論理名の取得 */
    public String getName() {
		return bdsLogicalName;
	}

  /*  バッチの継続実行可否の判定 */
    public boolean isContinue(){
       if(this.position < data.length){
           return true;
	}
	else{
           return false;
		}
	}

  /*(ユーザーメソッド)データの取得 */
    public Object getData(){
      return data[position++];
    }

}

データの入出力の部分(ここでは、geData()というユーザー・メソッド)に加えて、チェック・ポイントをハンドリングするための幾つかのメソッドを作成する必要があります。コンテナは、これらのメソッドを、次のように使用します。
コンテナは、指定されたタイミング毎に、externalizeCheckpointInformation()を呼んで、チェック・ポイント・データを入手し、これをDBに保存します。障害が発生した後の再始動では、コンテナはDBからチェック・ポイント・データを取り出し、internalizeCheckpointInformation()を呼んでチェックポイントデータを渡し、その後、positionAtCurrentCheckpoint()を呼んで、BDSへ状態の復旧を指示します。

バッチ・ジョブ・ステップの作成
次に、このBDSを使用してレコードを入力し、処理するバッチ・ジョブ・ステップを作成します。リスト3が、サンプル・プログラムです。結果は、標準出力に出力しています。

リスト3. バッチ・ジョブ・ステップのサンプルコード(HelloBatchJobStep)
(省略)
public class HelloBatchJobStep01 implements BatchJobStepInterface{
  final static String BDS_NAME="HelloBatchBDS";
  private Properties props = null;
  private HelloBatchBDS bds =null;
  private boolean isSuccess = false;

  /* プロパティのセット */
  public void setProperties(Properties props){
	       this.props = props;
  }

  /* プロパティの取得 */
   public Properties  getProperties(){
	        return props;
   }

  /*インスタンスの作成時に実行 */
  public void createJobStep(){
      try{
           // JOB IDとSTEP IDから、Job Step IDを作成
           JobStepID id= new JobStepID(
                           this.getProperties().getProperty(BatchConstants.JOB_ID),
                           this.getProperties().getProperty(BatchConstants.STEP_ID)
                         );
           //  Job Step IDから、バッチデータストリームを取得
           bds = (HelloBatchBDS)BatchDataStreamMgr.getBatchDataStream(
BDS_NAME, id.getJobstepid());
           if(bds == null ){
              throw new RuntimeException("Cannot get BDS : "+BDS_NAME);
           }
        }
        catch(BatchContainerDataStreamException ex){
            ex.printStackTrace();
	          throw new RuntimeException(ex);
       }
  }

  /* 各ステップで実行する処理。完了か継続かを戻り値で指示 */
  public int processJobStep(){
        
        // バッチデータストリームからデータを取得
	      String name = (String)bds.getData();

	     // データの加工&出力
	     System.out.println(">>> Hello "+name+"!! <<<");

        // 継続 又は 完了を指示するための、戻り値の選択
	      if(bds.isContinue()){
		    return BatchConstants.STEP_CONTINUE;
	      }
	     else{
		  isSuccess = true;
		  return BatchConstants.STEP_COMPLETE;
	     }
  } 

  /**
  * バッチ実行完了時の終了処理&成功可否を戻り値で返す
  */
  public int destroyJobStep(){
      return ( isSuccess?0:1);
  }
}

コンテナとのインターフェースは、以下のように動作します。

まず、初期化のフェーズで、createJobStep()が呼ばれます。この中で、BDSのインスタンスを得て、参照を変数bdsに保持します。読み込む1レコードの処理内容を記述するのが、processJobStep()です。その戻り値によって、コンテナは、処理の継続か停止かを判断します。BatchConstants.STEP_CONTINUEであれば継続、BatchConstants.STEP_COMPLETEであれば、終了します。

コンパイル
BDS(HelloBatchBDS)とバッチ・ジョブ・ステップ(HelloBatchJobStep01)を作成したら、javacでコンパイルします。コンパイルが完了したら、mysample.jarにアーカイブしてください。(リスト4)

リスト4. jarへのアーカイブ
 jar –cvf mysample.jar mysample

パッケージング
コンパイル&アーカイブが済んだら、作成したjarをパッケージ作成用ツールに渡して、earパッケージを作成します。以下が、実行例です。

リスト5. パッケージングの実行
 java -cp %WAS_HOME%\plugins\com.ibm.ws.batch.runtime.jar
 com.ibm.ws.batch.packager.WSBatchPackager -appname=SampleBatchApp
  -jarfile=mysample.jar -earfile=samplebatch -target=websphere

*注意:リスト5で指定しているクラスパスは、WAS V8の場合です。WAS V7+FeP for Modern Batchの場合は、クラスパスの設定が以下になります。

%WAS_HOME%\feature_packs\BATCH\plugins\com.ibm.ws.batch.runtime.jar

デプロイ
生成された、samplebatch.earを通常のJavaEEアプリケーションと同様に、WASにデプロイします。

*注意: 初めてバッチ・アプリケーションをデプロイした場合は、グリッド・エンド・ポイントの構成を有効化するために、サーバーをリスタートする必要があります。

xJCLの作成
作成したアプリケーションをバッチとして実行するためには、xJCLを作成してサブミットする必要があります。xJCLは、例えば、以下のようになります。

リスト6. xJCLのサンプル
<?xml version="1.0" encoding="UTF-8"?>
<job name="BatchSample" default-application-name="SampleBatchApp"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<jndi-name>ejb/com/ibm/ws/batch/SampleBatchAppBatchController</jndi-name>
	<step-scheduling-criteria>
		<scheduling-mode>sequential</scheduling-mode>
	</step-scheduling-criteria>
	<checkpoint-algorithm name="recordbased">
	       <classname>com.ibm.wsspi.batch.checkpointalgorithms.recordbased</classname>
	       <props>
			<prop name="recordcount" value="2" />
		</props>
	</checkpoint-algorithm>
	<results-algorithms>
		<results-algorithm name="jobsum">
		       <classname>com.ibm.wsspi.batch.resultsalgorithms.jobsum</classname>
		</results-algorithm>
	</results-algorithms>
	<job-step name="BatchSampleStep1">
		<classname>mysample.HelloBatchJobStep01</classname>
		<checkpoint-algorithm-ref name="recordbased" />
		<results-ref name="jobsum" />
		<batch-data-streams>
			<bds>
				<logical-name>HelloBatchBDS</logical-name>
				<impl-class>mysample.HelloBatchBDS</impl-class>
			</bds>
		</batch-data-streams>
	</job-step>
</job>

4.ジョブ管理コンソールによるジョブの操作

ジョブのサブミット
作成したxJCLをサブミットします。まず、先ほどのURL(http://<ホスト名>:<ポート>/jmc)にアクセスして、ジョブ管理コンソールにログインします。
左側のナビゲータ・ペインで「ジョブのサブミット」を選択し、ジョブのサブミット画面で、「ローカル・ファイル・システム」を選択して、参照ボタンをクリックし、サブミットするxJCLファイルを選択したら、「サブミット」ボタンをクリックします。

図5. ジョブのサブミット
図5. ジョブのサブミット

ジョブの状況の確認

サブミットしたジョブの状況は、ジョブ管理コンソールで「ジョブの表示」を選択して確認できます。これまでにサブミットしたジョブがリスト形式で表示され、それぞれの状態や実行ノードなどが確認できます。

図6. ジョブの表示
図6. ジョブの表示

ジョブ・ログの参照
サブミットしたジョブの状態が終了になったら、ジョブの表示において、ジョブIDのリンクをクリックして、ジョブ・ログを表示します(図7)。(必要なら、「ダウンロード」ボタンをクリックして、ジョブ・ログをダウンロードすることも可能です)

図7. ジョブ・ログの表示
図7. ジョブ・ログの表示

ジョブ・ログでは、サブミットされたxJCLに続いて、標準出力の内容や、バッチ・コンテナのメッセージ出力等々が記録されています。サンプル・プログラムのHelloメッセージ、及び、ジョブ・ステップの正常完了のメッセージ「Job [BatchSample:nnnnn] Step [BatchSampleStep1] finished with return code 0」が出力されていれば、バッチ実行は正常に完了しています。


5.Javaバッチの開発:テキスト・ファイルからの入力

次のステップとして、名前データをテキスト・ファイルから読み込むようにサンプルを変更します。まず考えられるのは、先のBDSクラスにファイル読込みの処理コードを付け加えることですが、実は、このような「お決まり」の処理は、BDSフレームワークを適用することで、もっと、お手軽に作成することができます。

BDSフレームワーク
バッチ処理には、往々にして決まった処理パターンがあります。例えば、テキスト・ファイルを1行ずつ読み込んで処理して、結果を1レコードずつファイルに追加する、といったケースは、良くある処理です。BDSフレームワークは、こういった「お決まり」パターンで必要とされる実装の多くを肩代わりし、開発者が、よりアプリケーション固有の実装に集中することができます。例えば、図8は、今述べた1つのテキスト・ファイルから入力して処理し、1つのテキスト・ファイルに出力する処理パターンに、BDSフレームワークを適用したケースを表しています。TextFileReader、TextFileWrite、及び、GenericXDBatchStepといったクラスは、フレームワークが提供します。図4と比較すると、これらのクラス群が、バッチ・コンテナとのインターフェース実装の一部を肩代わりし、開発対象のクラス(FileReaderPattern、FileWritePattern、BatchRecordProcessorがインターフェースになります)では、メソッド数が、大幅に少なくなっています。

図8. BDSフレームワーク(1入力テキスト・ファイル&1出力テキスト・ファイルの場合)
図8. BDSフレームワーク(1入力テキスト・ファイル&1出力テキスト・ファイルの場合)

BDSフレームワークは、ファイルだけでなくDBへの入出力を含めて、現在10種類のIOパターンをサポートしています。表1は、インフォセンターから抜粋したIOのパターンとそれに応じて提供されるサポート・クラスの一覧です。

表1. BDSフレームワーク・パターン及びサポート・クラス(インフォセンター抜粋)
パターン名説明サポート・クラス
JDBCReaderPatternJDBC接続を使用してデータベースからデータを取得する場合に使用されます。LocalJDBCReader
JDBCReader
CursorHoldableJDBCReader
JDBCWriterPatternJDBC接続を使用してデータベースにデータを書き込む場合に使用されます。LocalJDBCWriter
JDBCWriter
ByteReaderPatternファイルからバイト・データを読み取る場合に使用されます。FileByteReader
ByteWriterPatternファイルにバイト・データを書き込む場合に使用されます。FileByteWriter
FileReaderPatternテキスト・ファイルの読み取りに使用されます。TextFileReader
FileWriterPatternテキスト・ファイルへの書き込みに使用されます。TextFileWriter
RecordOrientedDataSet
-ReaderPattern
z/OS データ・セットの読み取りに使用されます。ZFileStreamOrientedTextReader
ZFileStreamOrientedByteReader
ZFileRecordOrientedDataReader
RecordOrientedDataset
-WriterPattern
z/OS データ・セットへの書き込みに使用されます。ZFileStreamOrientedTextWriter
ZFileStreamOrientedByteWriter
ZFileRecordOrientedDataReader
JPAReaderPatternOpenJPAを使用してデータベースからデータを取得する場合に使用されます。JPAReader
JPAWriterPatternJava Persistence API (JPA) 接続を使用してデータベースにデータを書き込む場合に使用されます。JPAWriter

BDSフレームワークによるファイル読み込みBDS作成
それでは、BDSフレームワークを適用して、テキスト・ファイルからの読込みを実装しましょう。サンプル(リスト7)をご覧ください。テキスト・ファイル入力のパターンであるFileReaderPatternを使っています。ファイルからの入力の機能が加わったのに、3章で作成したBDSクラスよりコード量は減っています。

リスト7.テキスト・ファイルを読込むBDS(HelloBatchFileReader)
(省略)
public class HelloBatchFileReader implements FileReaderPattern {

  /* 初期化 */
   public void initialize(Properties props){}

  /* ヘッダの処理(必要な場合)*/
   public void processHeader(BufferedReader reader) throws IOException{}

  /* ジョブステップ側にヘッダを渡す(必要な場合)*/
   public Object fetchHeader() { return null; }
         
  /* ファイルから読み込んだ1行の処理 */
   public Object fetchRecord(BufferedReader reader) throws IOException{
		String line = reader.readLine();
		return line;
   }
}

作成したクラスには、4つのメソッドがありますが、主要なメソッドは、fetchRcord()になります。fetchRecord()では、フレームワークから引数として渡されたBufferdReaderから1レコードを読み込み、バッチ・ステップに渡すデータに変換して戻り値として返します。例えば、実際のアプリケーションでは、CSVファイルの1行を読み込んで業務データ・オブジェクを生成するといった処理が考えられます。このサンプルでは、読み込んだ行=「名前」という想定で、読み込んだ行をそのまま戻り値としています。

バッチ・ジョブ・ステップの修正
入力用BDSの変更に応じてジョブ・ステップも、少々変更します(リスト8)。

リスト8.修正したバッチ・ジョブ・ステップ(HelloBatchJobStep02)
(省略)
public class HelloBatchJobStep02 implements BatchJobStepInterface{
  final static String BDS_NAME="HelloBatchFileReader";  

   /* インスタンスの作成時に実行 */
  public void createJobStep(){
      try{
           // JOB IDとSTEP IDから、Job Step IDを作成
           (省略)
           //  Job Step IDから、バッチデータストリームを取得
           bdsInputStream = (AbstractBatchDataInputStream)BatchDataStreamMgr.
getBatchDataStream(BDS_NAME, id.getJobstepid());

       }
       catch(BatchContainerDataStreamException ex){
            ex.printStackTrace();
	throw new RuntimeException(ex);
       }
  }

  /* 各ステップで実行する処理。完了か継続かを戻り値で指示 */
  public int processJobStep(){
        
        // バッチデータストリームからデータを取得
	if(bdsInputStream == null){
		System.err.out("No BDS input stream!!");
		return BatchConstants.STEP_COMPLETE;
	}
	try{
		String name = (String)bdsInputStream.read();

		if(name != null){

                    // データの加工&出力
	            System.out.println(">>> Hello "+name+"!! <<<");

                   // 戻り値「継続」でリターン
	           return BatchConstants.STEP_CONTINUE;
	         }
	         else{
                     // 戻り値「完了」でリターン
		     isSuccess = true;
		     return BatchConstants.STEP_COMPLETE;
                 }
	}
        catch(Exception ex){
            ex.printStackTrace();
            throw new RuntimeException("Exception occured in step setup", ex);
        } 
  } 
 (省略)
}

パッケージング
3章と同様に、クラスをコンパイルしたら、jarにアーカイブして、パッケージング・ツールでearを生成します。生成したearをアプリケーションの「更新」で、WASへデプロイします。

入力ファイルの準備
入力ファイルとして、名前を1行ずつ記述したファイルを作成します。

xJCLの変更
xJCLでは、ジョブ・ステップのBDSの記述、すなわち、<impl-class>で指定するBDSクラスを、フレームワークが提供するクラスTextFileReader に変更します。作成したBDSパターンのクラスHelloBatchFileReaderは、このBDSクラスに対するプロパティIMPLCLASSの値に設定します。また、プロパティFILENAMEに、入力ファイルのパスを指定します。

リスト9.修正したxJCLのジョブ・ステップ記述
	<job-step name="Step1">
		<classname>mysample.HelloBatchJobStep02</classname>
		<checkpoint-algorithm-ref name="recordbased" />
		<results-ref name="jobsum" />
		<batch-data-streams>
		  <bds>
			<logical-name>HelloBatchFileReader</logical-name>
			<props>
			   <prop name="IMPLCLASS" 
value="mysample.HelloBatchFileReader" />
			    <prop name="FILENAME" value="C:\\work\\indata.txt" />
			 </props>
		        <impl-class>
com.ibm.websphere.batch.devframework.datastreams.patterns.TextFileReader
</impl-class>
	       </bds>
	    </batch-data-streams>
	</job-step>

ジョブの実行・確認
前回同様に、ジョブ管理コンソールから修正したxJCLをサブミットします。終了後、ジョブ・ログを確認して、ファイルからの入力に基づいて処理が行われて、Helloメッセージが出力され、ジョブ・ステップが正常に完了したことを確認します。


6.Javaバッチの作成:テキスト・ファイルへの出力

前章では、名前データをファイルから入力するように処理内容を変更しました。今度は、これまで単に標準出力へ出力していたメッセージを、通常のファイルに出力するように変更しましょう。

出力用BDSの作成
入力と同様に、出力用BDSにも、BDSフレームワークのテンプレートが使用できます。テキスト・ファイル出力のパターンであるFileWriterPatternを使って作成したサンプルのコードが、リスト10です。

リスト10. テキスト・ファイルへ出力するBDS(HelloBatchFileWriter)
public class HelloBatchFileWriter implements FileWriterPattern {
   /* 初期化処理 */
public void initialize(Properties props){ }

   /* ヘッダの処理(必要な場合) */
   public void writeHeader(BufferedWriter writer, Object header) throws IOException{}

   /* ジョブ・ステップにヘッダを渡す(必要な場合)*/
   public void writeHeader(BufferedWriter writer) throws IOException{}

  /* ファイルへの1行の書き込み */
   public void writeRecord(BufferedWriter writer, Object record) throws IOException{
      String text=(String)record;
      writer.write(text);
      writer.newLine();
   }
}

ここでの主要なメソッドは、writeRecord()で、このメソッドが、レコードの書き込み処理を行います。writeRecord()は、入力データとして渡されるオブジェクト(バッチ・ジョブ・ステップが生成)から出力レコードを作成して、もう1つの引数であるBufferedWriterに書き込みます。サンプルでは、単に入力データをStringとして、BufferedWriterに書き込んで改行しています。
次に、バッチ・ジョブ・ステップの変更ですが、このように、入力が1つ、出力が1つ、それぞれ、BDSフレームワークのパターンで処理できる場合、バッチ・ジョブ・ステップにもテンプレートを適用してコード量を大幅に削減することができます。これは、ちょうどBDSフレームワークの図8の場合です。その場合、図にあるようにバッチ・ジョブ・ステップが実装するインターフェースは、BatchRecordProcessorになります。(これは、IOパターンには依存しません)
リスト11をご覧ください。実質的に実装が必要なのは、processRecord()メソッドだけです。processRecord()では、渡された入力レコードを処理して、出力レコードを戻り値で返します。

リスト11. レコード処理を行うバッチ・ジョブ・ステップ(HelloBatchRecordProcessor)
public class HelloBatchRecordProcessor implements BatchRecordProcessor {

    /* 初期化 */
    public void initialize(Properties props){}

    /* 1レコードの処理 */
    public Object processRecord(Object record) throws Exception {
        String input = (String)record;
        String output = "*** Hello "+input+"!! ***";
        return output;
    }

    /* 処理の完了処理 */
    public int completeProcessing() 
    {
        return 0;
    }
}

パッケージングとデプロイ
これまでと同様に、コンパイル及びパッケージングを行い、アプリケーションの更新機能でWASにデプロイします。

xJCLの修正
xJCLのジョブ・ステップを修正します。

① ジョブ・ステップの記述
クラスは、
com.ibm.websphere.batch.devframework.steps.technologyadapters.GenericXDBatchStep
になります。また、ユーザーが作成したRecordProcessorクラスを、プロパティ値BATCHRECORDPROCESSORへの値で渡す必要があります。

② 入力用BDSの記述
入力用BDSの記述は、殆どサンプル2と同じです。但し、BDS名は、inputStreamとする必要があります。

③ 出力用BDSの記述
出力用BDSの記述では、<impl-class>に、
com.ibm.websphere.batch.devframework.datastreams.patterns.TextFileWriteを指定します。BDS名は、outputStreamとする必要があります。作成したクラスを、プロパティ「IMPLCLASS」の値で渡す点は、入力用BDSと同様です。また、同様に、出力ファイルのパスを、プロパティ「FILENAME」に記述します。プロパティ値「AppendJobIdToFileName」は、出力ファイル名の最後にジョブIDを付加するかどうかをtrueまたはfalseで指示します。

リスト12. 修正したxJCLのジョブ・ステップ記述
<job-step name="Step1">
  <classname>com.ibm.websphere.batch.devframework.steps.technologyadapters.Generic
XDBatchStep</classname>
    <checkpoint-algorithm-ref name="recordbased" />
    <results-ref name="jobsum" />
    <batch-data-streams>
      <bds>
        <logical-name>inputStream</logical-name>
        <props>
          <prop name="IMPLCLASS" value="mysample.HelloBatchFileReader" />
          <prop name="FILENAME" value="c:\\work\\indata.txt" />
          <prop name="AppendJobIdToFileName" value="false" />
        </props>
        <impl-class>com.ibm.websphere.batch.devframework.datastreams.patterns.
TextFileReader</impl-class>
      </bds>
      <bds>
        <logical-name>outputStream</logical-name>
        <props>
          <prop name="IMPLCLASS" value="mysample.HelloBatchFileWriter" />
          <prop name="FILENAME" value="c:\\work\\outdata.txt" />
          <prop name="AppendJobIdToFileName" value="false" />
        </props>
        <impl-class>com.ibm.websphere.batch.devframework.datastreams.patterns.
TextFileWriter</impl-class>
      </bds>
    </batch-data-streams>
    <props>
      <prop name="BATCHRECORDPROCESSOR"
value="mysample.HelloBatchRecordProcessor" />
    </props>  
</job-step>

ジョブの実行・確認
修正したxJCLを、ジョブ管理コンソールからサブミットします。出力用BDSの記述で設定した通りのパス名でファイルが作成され、ファイルに1行ずつメッセージが出力されることを確認します。

以上でサンプル開発の解説は終わりです。プログラミング・モデルを直接実装する最初のサンプルに比べて、最後のプログラムは、とても簡潔に感じたのではないでしょうか?実際、最後のサンプルは最初に比べて、ファイル入出力を行う分、機能的にはより複雑なはずですが、BDSフレームワークを適用することで、プログラムは、かえって見通しがよく、かつ、コード量も少ない実装になっています。


7.Javaバッチに関連する他の製品

この章では話を変えて、標準機能としてWASに搭載されたJavaバッチを補足、或いは、補強するIBMの関連ソフトウェア2つを紹介します。

WebSphere Extended Deployment Compute Grid
WASのバッチ機能の本家に当たる製品であり、様々な機能拡張を提供します。主要な拡張機能としては、以下があります。(詳細は、参考文献を参照ください)

① 分割並列処理機能
ジョブを分割して並列実行するために、Parallel Job Manager(PJM)機能を提供します。PJMによって、1つのジョブの配下にサブ・ジョブを生成してサブミットし、その実行状態を一括管理することができます。処理を分散して並列処理することで、大規模なバッチ処理の処理時間を大幅に削減することが可能になります。

② エンタープライズ・スケジューラとの統合
多くの企業では、多数のジョブが複雑に連携して稼動しており、このようなジョブ・ネットワークを管理するためにエンタープライズ・スケジューラが使用されています。Javaバッチを、企業のジョブ・ネットに組み込むために、WSGrid機能は、エンタープライズ・スケジューラとWASとの連携機能を提供します。

③ 日付・時刻によるスケジュール実行機能
予め指定した日付・時刻に、バッチ・ジョブを実行する機能です。ジョブ管理コンソール画面も拡張されて、開始時刻を設定できるようになります。

④ ジョブ・クラスによるワークロードの制御
ジョブをジョブ・クラスにグルーピングして、ジョブの最大実行時間、最大並行実行数、ジョブ・ログの保存期間等をジョブ・クラスごとに制御することができます。

⑤ WebSphere Virtual Enterpriseと連携した優先制御
実行依頼者、 実行依頼者グループ、ジョブ名、ジョブ・クラス、アプリケーション名、アプリケーション・タイプ、プラットフォーム、実行日時等により、ジョブの目標完了時間と重要度を設定することができます。スケジューラは、この設定を参考にジョブをディスパッチします。(z/OS環境では、WLM機能と連携することも可能です)

⑥ アカウント情報の提供
課金のためのアカウント情報を、ジョブ管理DBに記録することが可能です。z/OS環境では、この課金情報をSMFに書き込むことが可能です。

⑦ COBOLモジュールの実行(z/OS)
z/OS環境では、COBOLコンテナで稼動するCOBOLモジュールを、Javaスタブを通じて呼び出すことができます。

最後の⑦の機能は、V8で追加された機能で、従来のCOBOL資産を、Javaバッチから活用できる手段が提供されることになります。V8では、分散環境のバッチ処理機能を高度化することに加えて、メインフレーム環境特有の機能を活用する機能も強化されていると言えます。

Rational Application Developer for WebSphere Software
当記事では、読者が既存のJava開発環境を使用する前提で、Javaバッチの開発ステップを述べてきました。一方、Rational Application Developer for WebSphere Software V8を使用すれば、更に、効率的なJavaバッチ開発を行うことができます。次の機能が提供されています。

① バッチ・プロジェクト
xJCL、ジョブ・ステップ、BDSを一体的に開発する環境として、バッチ・プロジェクトを作成できます。WAS V7+ModernBatch FePとWAS V8の両環境に対応します。

② ジョブ作成ウィザード
新規ジョブを作成する場合、xJCL、ジョブ・ステップ、BDSを一度に作成することが可能です。BDSフレームワークを使用した開発、或いは、使用しないカスタム開発の両方に対応します。

③ xJCLエディタ
xJCL専用のエディタにより、効率的にxJCLを作成・編集することができます。

④ パッケージング
バッチ用のearファイルへのパッケージングも自動的に行えます。

⑤ テスト環境
WAS V7+Modern Batch FeP、及び、WAS V8が同梱されており、テスト及びデバッグを含む一連のライフ・サイクルをサポートします。

Rational Application Developer for WebSphere Software V8は、バッチ以外にもJava EEコンポーネントやOSGiモジュール開発のサポート機能など、WASで稼動する各種アプリケーションの開発を強力に支援します。詳細は、参考文献を参照ください。


おわりに

バッチ処理は、オンライン処理に比べて裏方のイメージが強く、これまでは、あまり光が当ってこなかった感があります。しかし、バッチ処理は企業の基幹業務に関わる重要な処理を担うケースが多々あり、処理の遅延がオンラインに多大な影響を与える場合もあります。また、ビジネスのアジリティ向上には、従来の月次や週次といったバッチ処理の見直しは避けて通れません。今回のWAS V8への標準搭載やWebSphere Extended Deployment Compute Gridの新版のリリースを契機に、バッチへの関心が改めて高まることも予想されます。当記事では、Javaバッチの実際の開発ステップを追いながら、プログラミング・モデル及び、開発効率を大きく向上するBDSフレームワークを簡単に解説しました。WASには無料の開発者版もあるので、当記事を参考にして是非Javaバッチ開発にチャレンジしてみてください。


ダウンロード

内容ファイル名サイズ
サンプルコードSample.zip8KB

参考文献

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=WebSphere
ArticleID=696196
ArticleTitle=作ってみようJavaバッチ ~ WebSphere Application Serverのバッチ機能を使う
publish-date=06302011