<commit_before>=========================
Asakusa DSLユーザーガイド
=========================

この文書では、Asakusa DSLおよびDSLコンパイラの利用方法について紹介します。

..  todo:: Asakusa DSL > User Guide
<commit_msg>Added a document "Asakusa DSL users guide" (#25)<commit_after>=========================
Asakusa DSLユーザーガイド
=========================

この文書では、Asakusa DSLおよびDSLコンパイラの利用方法について紹介します。

=======


DSLの概要
=========
Asakusa DSLは次の3種類のDSLで構成されています。

`Operator DSL`_
     Javaでデータ操作の処理を「演算子」として記述する

`Flow DSL`_
     演算子を組み合わせて「データフロー」を記述する

`Batch DSL`_
     データフローを組み合わせて「業務バッチ」を記述する


アプリケーションの設計と実装
----------------------------
Asakusa DSLは、データフロー形式でのバッチ処理設計から
素直な実装を作成できることを目標に設計しています。

データフローは大雑把にいえば、入力データをプロセスで加工して出力するという流れを取ります。
`Flow DSL`_ ではそのようなデータフローを直接記述でき、データを加工するプロセスは
`Operator DSL`_ で通常のJavaを使って手続き的に記述できます。
そのため、設計の段階からデータフローや処理の単位


Asakusa Frameworkを利用したバッチ処理の設計については、
`ITpro: Hadoop向け基幹バッチ分散処理ソフト「Asakusa」の全貌`_ などを参照してください。

.. _`ITpro: Hadoop向け基幹バッチ分散処理ソフト「Asakusa」の全貌` : http://itpro.nikkeibp.co.jp/article/COLUMN/20110322/358560/

..  todo:: バッチ処理の設計について書きおろす


DSLコンパイラ
-------------
Asakusa DSLで記述したプログラムは、Asakusa Frameworkに付属の
Ashigelコンパイラを使ってMap Reduceのプログラムに変換します。
このコンパイラは2種類のコンパイラから構成されています。

`Operator DSLコンパイラ`_
    `Operator DSL`_ をコンパイルして、 `Flow DSL`_ で使える部品を生成するコンパイラ。
    Javaの注釈プロセッサとして提供している。

`Batch DSLコンパイラ`_
    `Batch DSL`_ をコンパイルして、Map Reduceのプログラムを生成するコンパイラ。
    コマンドラインインターフェースを提供している。

なお、DSLが3層であるのに対し、コンパイラは2層のみに対応しています。
直接のコンパイルを行わない `Flow DSL`_ についても、テスト時には
単体でコンパイルして実行を行えるようになっています。


Operator DSL
============
Operator DSLは「 `演算子`_ 」と呼ばれるデータフロー処理の最小単位を記述するDSLです。
それぞれの演算子では単一のデータを表す「レコード」や、それらをグループ化した「グループ」に対する処理を
Javaのプログラムとして記述できます。


演算子
------
Asakusa DSLにおいて、演算子は
「複数のデータセットを入力して加工し、複数のデータセットを出力するもの」
といえます。
この演算子を組み合わせて大きなデータフローを構築していくのが、
このフレームワークでの標準的な設計開発手法です。

演算子には「種類」というものがあり、それぞれ入出力や処理の内容に制限を与えています。
:doc:`operators_ja` から演算子の種類を選択し、実際の処理内容を決めていきます。

それぞれの演算子は、Askausaが提供するOperator DSLを使って記述します。
DSLといっても難しいものではなく、通常のJavaのメソッド宣言に
演算子用のメタデータを指定する形で記述できます。
このように演算子を定義するメソッドをAsakusaでは `演算子メソッド`_ とよび、
それらを宣言しているクラスを `演算子クラス`_ と呼んでいます。


演算子クラス
~~~~~~~~~~~~
演算子クラスは `演算子メソッド`_ を定義するためのJavaのクラスです。
基本的なJavaの *抽象クラス* ですが、以下のような制約があります。

* トップレベルクラスとして宣言する
* ``public`` , ``abstract`` として宣言する
* 型引数を宣言しない
* 明示的な親クラスやインターフェースを指定しない
* 明示的なコンストラクタを宣言しない
* 演算子メソッド以外の ``public`` メソッドを宣言しない

上記に含まれない、たとえばフィールドの宣言などは自由に行えます。

以下は演算子クラスの例です。

..  code-block:: java

    package com.example.operator;

    public abstract class ExampleOperator {
        ...
    }

演算子メソッド
~~~~~~~~~~~~~~
演算子メソッドはそれぞれの演算子を定義するメソッドで、
Javaの公開メソッドに演算子注釈と呼ばれる注釈を指定したものです。

* 演算子注釈が一つだけ付与されている
* 付与された演算子注釈に適した引数や返戻型が宣言されている
* ``public`` として宣言する
* ``static`` として宣言しない
* 演算子クラス内の他のあらゆる演算子メソッドと名前が衝突 [#]_ しない

演算子の一覧や、演算子注釈については :doc:`operators_ja` を参照してください。

以下は、演算子メソッドの例です。

..  code-block:: java

    public abstract class ExampleOperator {

        /**
         * レコードの値に100を設定する。
         * @param hoge 更新するレコード
         */
        @Update
        public void edit(Hoge hoge) {
            hoge.setValue(100);
        }
        ...
    }

..  note::
    現在はJavaをホストに演算子の宣言を行っていますが、一部の演算子の生産性に難があるため
    将来は別のDSLと併用することを計画しています。
    問題と感じている演算子は主に結合や変換など、データモデルと強く連携したものです。
    これらは静的な「型名」の概念があるJavaなどの言語では取り扱いが難しく、
    中間データ用の型を多数用意するかまたは複雑なメタデータの指定が必要になると考えています。

..  [#] この名前衝突の判定はアンダースコア、大文字、小文字を無視します。

キー注釈
~~~~~~~~
データモデルのグループ化条件やソート条件を記載するには、
演算子の仕様に従って注釈 ``Key`` をメソッド引数などに指定します。
この注釈には、それぞれ下記のような要素を記載できます。

..  list-table:: ``@Key`` の要素
    :widths: 4 10 10
    :header-rows: 1

    * - 要素名
      - 記載内容
      - 例
    * - ``group``
      - グループ化に利用するプロパティ名の一覧。
        これらのフィールドが全て同じものでグループを構成する。
        空の配列を指定すると全てを単一のグループにまとめる。
      - ``group = "name"``
    * - ``order``
      - 順序付けに利用するプロパティ名と、順序の一覧。
        フィールド名の後に"ASC"や"DESC"で順序を指定する。
        指定しない場合の整列順序は実装依存。
      - ``order = "age ASC"``

それぞれに指定するプロパティ名は、下記のいずれの形式も利用できます。

``snake_case``
    すべての語を小文字で指定し、 ``_`` (アンダースコア)で区切る。
    DMDLの名前と同じ形式 (推奨)。
``UPPER_CASE``
    すべての語を大文字で指定し、 ``_`` (アンダースコア)で区切る。
    データベースのカラム名でよく利用される形式。
``camelCase`` (Lower Camel Case)
    単語の先頭のみを大文字で指定し、先頭の単語だけすべて小文字で指定する。
    Javaのフィールド名等の標準規約と同じ形式。
``PascalCase`` (Upper Camel Case)
    単語の先頭のみを大文字で指定する。
    Javaのクラス名等の標準規約と同じ形式。

..  note::
    このプロパティの命名規約により、利用可能なプロパティ名にいくつかの制限が設けられます。
    具体的には、 ``HTMLString`` のよう形式のプロパティ名が期待した名前にならない、
    ``value_0`` のように単語の先頭がアルファベットでないものを正しく認識できない、
    などが挙げられます。


単一の演算子の中に複数の ``@Key`` を指定する場合には、次のことに注意して下さい。

* それぞれのキーに出現する ``group`` の項目は、同じ個数でなければならない
* ``group`` の各項目は、それぞれのキーにおいて以下のように計算を行う

    * 同じ位置のそれぞれの項目で等価比較を行う
    * 同じ位置のそれぞれの項目は、完全に同じ型でなければならない

* ``order`` の項目については上記のような制約はない

それぞれの要素に複数の条件を指定するには、
プロパティ名や順序を ``group = { "a", "b", "c" }`` のようにカンマ区切りで指定します。

..  code-block:: java

    // 名前でグループ化
    @Key(group = "name")

    // 名前と性別でグループ化
    @Key(group = { "name", "sex" })

    // 名前でグループ化し、年齢の昇順で整列
    @Key(group = "name", order = "age ASC")

    // 名前でグループ化し、収入の昇順, 年齢の降順で整列
    @Key(group = "name", order = { "income ASC", "age DESC" })

    // 全てを単一のグループにまとめ、回数の降順で整列
    @Key(group = {}, order = "count DESC")


キーの指定が必要な演算子については、 :doc:`operators_ja` を参照してください。

演算子の多相化
~~~~~~~~~~~~~~
演算子メソッドは入出力するデータモデルに、クラス型以外にもインターフェース型を指定できます。
ただし、指定できるインターフェースは射影モデル [#]_ のみで、演算子メソッドの型引数を宣言してその上限境界に指定します。

..  code-block:: java

     @Update
     public <T extends Something>
     void example(T model) {
          model.setValue(100);
     }

詳しくは :doc:`generic-dataflow_ja` を参照してください。

..  [#] :doc:`../dmdl/user-guide_ja`

フレームワークAPI
-----------------
フレームワークAPIは、演算子メソッドの中で利用できるAsakusa Frameworkが提供するAPI群です。
これらのAPIはいずれも演算子クラスの外からは*利用できません*。

..  note::
    Asakusa DSLのうち、Batch DSLとFlow DSLで記述したJavaのプログラムはいずれも *コンパイル時に* 処理されます。
    対して、Operator DSLで記述したプログラムはアプリケーションの実行時に処理されます。
    フレームワークAPIはいずれもアプリケーションの実行時のみに有効で、コンパイル時には無効化されています。
    上記の理由で、 `Flow DSL`_ や `Batch DSL`_ からこれらのAPIを利用できません。

..  attention::
    実装上の理由で、現在 (0.2.0) はCombinerの内部からフレームワークAPIを利用できません。
    これは、畳み込み演算子 ( ``@Fold`` ) を利用し、かつ `Batch DSLコンパイラ`_ の
    コンパイルオプションなどでCombinerの利用を可能にしている場合に問題が発生します。

コンテキストAPI
~~~~~~~~~~~~~~~
コンテキストAPIは、バッチ起動時の引数を演算子内で利用するための仕組みを提供します。
バッチ起動時には文字列のキー名と値のペアを複数指定でき、
コンテキストAPIを利用するとキー名に対応する値を演算子の中から参照できます。

このAPIは ``BatchContext`` [#]_ クラスのメソッドから利用します。

..  list-table:: コンテキストAPIのメソッド
    :widths: 5 20
    :header-rows: 1

    * - メソッド名
      - 概要
    * - get
      - 指定したキー名に対応する値を参照する

また、バッチ引数以外にもあらかじめ宣言された変数を利用できます。

..  list-table:: あらかじめ宣言された変数
    :widths: 5 20
    :header-rows: 1

    * - 変数名
      - 概要
    * - ``user``
      - 現在のユーザ名。
    * - ``batch_id``
      - 実行中のバッチID。
        同一の `バッチ`_ に対しては常に同じ値になる。
    * - ``flow_id``
      - 実行中のフローID。
        同一の `ジョブフロー`_ に対しては常に同じ値になる。
    * - ``execution_id``
      - 現在の `ジョブフロー`_ に対する実行ID。
        同一のバッチIDやフローIDに対しても、ジョブフローの実行のたびに変化する。
        同一ジョブフローの実行中は必ず同じ値で、トランザクションを識別するために利用できる。

..  [#] ``com.asakusafw.runtime.core.BatchContext``

レポートAPI
~~~~~~~~~~~
レポートAPIは、バッチ実行時に発生したエラーや警告などをレポートする仕組みを提供します。
標準的な実装では、レポートはHadoopのログ機構にリダイレクトされます。

このAPIは ``Report`` [#]_ のクラスメソッドから利用します。

..  list-table:: レポートAPIのメソッド
    :widths: 5 20
    :header-rows: 1

    * - メソッド名
      - 概要
    * - error
      - 「エラー」レベルのレポート
    * - warn
      - 「警告」レベルのレポート
    * - info
      - 「情報」レベルのレポート

致命的な状況に対するレポートの仕組みも用意していますが、このレポートによって処理の流れに影響が出ることはありません。
エラーによって処理を強制終了させたい場合などでは、ランタイム例外を演算子メソッドからスローするなどの方法が必要です。

..  [#] ``com.asakusafw.runtime.core.Report``

..  attention::
    特定のデータに対してレポートのみを行い、その結果を最終的に出力しない場合、
    コンパイラの最適化によって演算子の処理が省略されてしまう場合があります。
    上記のような演算子メソッドには、最適化を抑止する注釈 ``Sticky`` を併せて指定してください。

..  hint::
    ロギング演算子の利用も検討してください。
    この演算子は内部的にこのレポートAPIを利用し、自動的に省略の最適化を抑止しています。

..  note::
    連携するワークフローエンジンによっては、
    このAPIで通知したレポートを何らかの形で拾い上げて利用者に通知してくれるかもしれません。
    標準的な実装である「Experimental Shell Script」では特に何も行いません。

Operator DSLコンパイラ
----------------------
Operator DSLコンパイラは作成した `演算子クラス`_ をコンパイルして
実行時に必要なクラスや `Flow DSL`_ に必要なクラスを生成します。

このコンパイラは、Javaの `注釈プロセッサ`_ の仕組みの上に構築しています。
そのため、Operator DSLコンパイラとそれの依存ライブラリを ``javac`` 
コマンドのクラスパスに指定することで、自動的にOperator DSLコンパイラが起動します。

..  attention::
    Operator DSLコンパイラは、後続のアプリケーション開発で必要なクラスを自動生成します。
    プロジェクトをクリーンビルドする際には、必要なクラスが一時的に足りない状態であるため、
    コンパイル順序によっては「クラスが見つからない」等の警告メッセージが表示されます。
    しかし、javacには「ラウンド」という概念があり、現在の処理のラウンドでクラスが見つからなくても、
    コンパイル中に新しく生成されたソースプログラムを含めて次のラウンドでさらにコンパイルを実行します。
    このため、最終的には正しくコンパイルできるソースプログラムであっても、
    一時的に警告メッセージが表示されてしまうようです。

..  note::
    Operator DSLコンパイラに注釈プロセッサの仕組みを採用した理由は、主にIDEとの親和性です。
    注釈プロセッサはJavaコンパイラの一部のようにふるまうため、注釈プロセッサ内で発生したエラーを
    コンパイルエラーのようにIDE上に表示させています。

.. _`注釈プロセッサ`: http://www.jcp.org/en/jsr/detail?id=269

演算子実装クラス
~~~~~~~~~~~~~~~~
演算子実装クラスは、 `演算子クラス`_ を継承した実装クラスです。
演算子クラスは抽象クラス (abstract class) として宣言し、いくつかの演算子メソッドは
本体を持たない抽象メソッドとして宣言していました。

演算子クラスそのものは抽象クラスのためインスタンスを生成できず、
実際に利用できないため、演算子実装クラスは具象クラスとして生成されます。
また、抽象メソッドとして宣言した演算子メソッドに対して、
オーバーライドした具象メソッドを生成します。

演算子実装クラスは、もとの演算子クラスの末尾に ``Impl`` をつけた名前で生成されます。
演算子メソッドに対する単体テストを行いたい場合には、生成された演算子実装クラスを
インスタンス化して行うことを推奨しています。

..  caution::
    ここで生成される具象メソッドは、実行時に利用されないダミーの実装である場合があります。
    また、生成される実装はコンパイラのバージョンが変わった際に内容が変更される場合もあります。
    それらの演算子メソッドに対する単体テストは行うべきではありません。

演算子ファクトリ
~~~~~~~~~~~~~~~~
演算子ファクトリは、 `演算子クラス`_ に宣言された演算子を
Flow DSLから利用できるようにするためのクラスです。
このクラスには、次の2つの要素が宣言されます。

演算子オブジェクトクラス
    Flow DSLでは、データフロー上の演算子を表すために「演算子オブジェクト」というものを利用します。
    これは、演算子のデータフロー内での接続状態を表し、
    さらにその演算子の出力を表す「ポート」をフィールドとして保持しています。
    演算子オブジェクトクラスはこのオブジェクトの元になるクラスで、
    演算子ファクトリの内部クラスとして宣言されます。
演算子ファクトリメソッド
    上記の演算子オブジェクトを生成するファクトリメソッドです。
    このメソッドは、演算子への入力を表す「ポート」を引数にとります。

演算子実装クラスは、もとの演算子クラスの末尾に ``Factory`` をつけた名前で生成されます。
また、演算子ファクトリメソッドはもとの演算子メソッドと同じ名前で、
演算子オブジェクトクラスはもとの演算子メソッドをJavaのクラス名の規約に変換した名前 [#]_ がつけられます。

`演算子の多相化`_ を行っている場合、対応する演算子オブジェクトクラスとファクトリメソッドには
それぞれもとの演算子メソッドで宣言した型引数が自動的に宣言されます。

..  note::
    このようなトリッキーな仕組みを採用しているのは、Javaに「メソッドをオブジェクトとして取り扱う」という
    方法が提供されていないためです。
    Asakusa DSLの演算子は「関数」に近い概念をもとに設計しており、
    Flow DSLでデータフローを構築することは、これらの関数を合成してひとつの
    巨大な関数を構築することに似せています。
    この関数を表すメソッドをオブジェクトとして取り扱うために、
    演算子オブジェクトやそれを生成するファクトリメソッドの概念を導入しました。

..  [#] メソッド名の最初の文字を大文字に変換します

フロー演算子
~~~~~~~~~~~~
Operator DSLコンパイラは、 `フロー部品`_ に対する演算子 (フロー演算子) も生成します。
フロー部品には「 `演算子実装クラス`_ 」が不要であるため、
「 `演算子ファクトリ`_ 」のみを生成します。
通常の演算子ファクトリとは次のような相違があります。

* 演算子ファクトリメソッド名は常に ``create``
* 演算子オブジェクトクラス名はフロー部品の名前と同じ

なお、フロー演算子については :doc:`operators_ja` を参照してください。


Flow DSL
========
Flow DSLは演算子を組み合わせてデータフローの構造を記述するDSLです。
このDSLではデータフローの構造を非循環有向グラフ (Directed Acyclic Graph: DAG)を
構造の通りにそのまま記述できます。

Flow DSLで記述できる構造は2種類あり、それぞれ異なる性質を持ちます。

`ジョブフロー`_
    外部システムからデータを取り出して、外部システムにデータを書き出すデータフロー。
    データフローの入出力にはそれぞれ
    `インポータ記述`_ と `エクスポータ記述`_ を付与して
    外部と連携する方法を記述する。
`フロー部品`_
    データフローそのものを演算子として定義する。
    ここで記述したデータフローは、Flow DSLで演算子として利用できる。

いずれの構造においても、Flow DSLではデータフローの入出力と
演算子の入出力をつなぎ合わせて、データ処理の流れを表します。

ジョブフロー
------------
ジョブフローはFlow DSLのトップレベルの要素で、
外部システムからデータを読み出し、データを加工して、外部システムにデータを
書き戻すという一連のデータ処理を記述できます。

外部システムとの連携は `インポータ記述`_ や `エクスポータ記述`_ で
それぞれ入出力方法を記述します。
また、外部入出力と `Operator DSL`_ で作成した演算子の入出力を
`フロー記述メソッド`_ 内で組み合わせて、データフローの構造を記述します。

インポータ記述
~~~~~~~~~~~~~~
インポータ記述はジョブフローの入力もととなるデータソースを記述するクラスです。
データソースごとに指定されたクラスを継承して、必要な情報を記載します。

Asakusa Frameworkは標準でThunderGateというデータソースを提供しています。
詳しくは :doc:`with-thundergate_ja` を参照してください。

..  caution::
    このメソッドは、 `Batch DSLコンパイラ`_ の *コンパイル中に* 起動されます。
    そのため、 `フレームワークAPI`_ はこの中では利用できません。

..  note::
    インポータ記述はいずれも ``ImporterDescription`` [#]_ インターフェースの
    実装クラスとなります。ただし、このインターフェースだけを実装しても
    データソースを利用することはできません。
    これらは、 `Operator DSLコンパイラ`_ のコンパイラプラグインを追加することで、
    新しいデータソースを利用できるようになります。

..  [#] ``com.asakusafw.vocabulary.external.ImporterDescription``

エクスポータ記述
~~~~~~~~~~~~~~~~
エクスポータ記述はジョブフローの結果を出力する先となるデータソースを記述するクラスです。
データソースごとに指定されたクラスを継承して、必要な情報を記載します。

Asakusa Frameworkは標準でThunderGateというデータソースを提供しています。
詳しくは :doc:`with-thundergate_ja` を参照してください。

..  caution::
    このメソッドは、 `Batch DSLコンパイラ`_ の *コンパイル中に* 起動されます。
    そのため、 `フレームワークAPI`_ はこの中では利用できません。

..  note::
    エクスポータ記述はいずれも ``ExporterDescription`` [#]_ インターフェースの
    実装クラスとなります。インポータ記述と同様に、このインターフェースだけを実装しても
    データソースを利用することはできません。

..  [#] ``com.asakusafw.vocabulary.external.ExporterDescription``

ジョブフロークラス
~~~~~~~~~~~~~~~~~~
それぞれのジョブフローは、データフローのベースクラスである
``FlowDescription`` [#]_ を継承したJavaのクラスとして宣言します。
このクラスには以下のような制約があります。

* ``public`` として宣言されている
* ``abstract`` として宣言されていない
* ``FlowDescription`` を継承する
* 注釈 ``JobFlow`` [#]_ を付与する
* 型引数を宣言していない
* 明示的なコンストラクターを一つだけ宣言する

また、注釈 ``JobFlow`` の要素 ``name`` にこのバッチの名前を指定します。
ここで指定する名前は、 Javaの変数名のうち、ASCIIコード表に収まるもののみでなければなりません。

以下はジョブフロークラスの例です。

..  code-block:: java

    package com.example.business.jobflow;

    import com.asakusafw.vocabulary.flow.*;

    @JobFlow(name = "stock")
    public class StockJob extends FlowDescription {

    }

..  [#] ``com.asakusafw.vocabulary.flow.FlowDescription``
..  [#] ``com.asakusafw.vocabulary.flow.JobFlow``

ジョブフローコンストラクタ
~~~~~~~~~~~~~~~~~~~~~~~~~~
ジョブフローの入出力は、ジョブフロークラスのコンストラクタで宣言します。
これには次のような制約があります。

* publicとして宣言されている
* 型引数を宣言していない
* ``In`` [#]_ 型の仮引数を一つ以上宣言し、それぞれ型引数にデータモデル型を指定する
* ``Out`` [#]_ 型の仮引数を一つ以上宣言し、それぞれ型引数にデータモデル型を指定する
* ``In`` , ``Out`` 以外の仮引数を宣言しない 

それぞれの ``In`` 型の引数は、ジョブフローへの1つ分の入力を表しています。
この仮引数には、注釈 ``Import`` [#]_ を付与し、要素 ``name`` に入力の名前を、
要素 ``description`` に `インポータ記述`_ のクラスリテラルを指定します。
ここで指定したインポート処理の結果が、この入力を通して利用できます。

同様に、それぞれの ``Out`` 型の引数は、ジョブフローからの1つ分の出力を表しています。
この仮引数には、注釈 ``Export`` [#]_ を付与し、要素 ``name`` に出力の名前を、
要素 ``description`` に `エクスポータ記述`_ のクラスリテラルを指定します。
この出力に対するジョブフローの結果が、エクスポート処理で書きだされます。

それぞれに指定する ``Import`` や ``Export`` にはそれぞれ次のような制約があります。

* 要素 ``name`` にはJavaの変数名のうち、ASCIIコード表に収まるもののみ指定できる
* それぞれの要素 ``name`` に指定する文字列が重複しない
* 要素 ``description`` に指定した記述と、型引数のデータモデルの型が一致する

..  note::
    ``name`` が重複してはいけない範囲は、それぞれの ``Import`` と ``Export`` の中のみです。
    ``Import`` と ``Export`` の組み合わせで重複しても構いません。

以下はジョブフローコンストラクタの例です。

..  code-block:: java

    In<Shipment> shipmentIn;
    In<Stock> stockIn;
    Out<Shipment> shipmentOut;
    Out<Stock> stockOut;

    /**
     * コンストラクタ。
     * @param shipmentIn 処理対象の注文情報
     * @param stockIn 処理対象の在庫情報
     * @param shipmentOut 処理結果の注文情報
     * @param stockOut 処理結果の在庫情報
     */
    public StockJob(
            @Import(name = "shipment", description = ShipmentFromDb.class)
            In<Shipment> shipmentIn,
            @Import(name = "stock", description = StockFromDb.class)
            In<Stock> stockIn,
            @Export(name = "shipment", description = ShipmentToDb.class)
            Out<Shipment> shipmentOut,
            @Export(name = "stock", description = StockToDb.class)
            Out<Stock> stockOut) {
        this.shipmentIn = shipmentIn;
        this.stockIn = stockIn;
        this.shipmentOut = shipmentOut;
        this.stockOut = stockOut;
    }

..  [#] ``com.asakusafw.vocabulary.flow.In``
..  [#] ``com.asakusafw.vocabulary.flow.Out``
..  [#] ``com.asakusafw.vocabulary.flow.Import``
..  [#] ``com.asakusafw.vocabulary.flow.Export``

フロー記述メソッド
~~~~~~~~~~~~~~~~~~
データフローでの処理容は、 ``FlowDescription`` クラスの ``describe`` メソッドをオーバーライドして記述します。
ここでは、コンストラクタで受け取った入出力と、 `Operator DSL`_ で記述した演算子を組み合わせて
データ処理の流れを記述します。

作成した演算子を利用するには、その演算子クラスに対応する `演算子ファクトリ`_ を経由します。
また、「コア演算子」という組み込みの演算子ファクトリも用意されています。
コア演算子については :doc:`operators_ja` を参照してください。

以下は、フロー記述メソッドの例です。

..  code-block:: java

    In<Shipment> shipmentIn;
    In<Stock> stockIn;
    Out<Shipment> shipmentOut;
    Out<Stock> stockOut;

    @Override
    protected void describe() {
        CoreOperatorFactory core = new CoreOperatorFactory();
        StockOpFactory op = new StockOpFactory();
       
        // 処理できない注文をあらかじめフィルタリング
        CheckShipment check = op.checkShipment(shipmentIn);
        core.stop(check.notShipmentped);
        core.stop(check.completed);
       
        // 在庫引当を行う
        Cutoff cutoff = op.cutoff(stockIn, check.costUnknown);
       
        // 結果を書き出す
        shipmentOut.add(cutoff.newShipments);
        stockOut.add(cutoff.newStocks);
    }

..  caution::
    このメソッドは、 `Batch DSLコンパイラ`_ の *コンパイル中に* 起動されます。
    そのため、 `フレームワークAPI`_ はこの中では利用できません。

..  note::
    フロー記述メソッドの記述は、主にデータフローの設計書を意識しています。
    設計書に記載されたデータフローの構造のうち、プロセスを演算子に置き換え、
    「この演算子の入力は、どこのデータを使えばいいか」ということを意識しながら
    演算子を配置していくことで、目的のデータフローを記述できます。
    ただし、グラフ構造をテキストで記述するとやはり読みにくくなってしまうため、
    テキスト以外の記述方法も検討しています。

フロー部品
----------
フロー部品は名前のとおり「データフローの部品」を定義する構造です。
ここで定義したデータフローは、ほかのデータフローから
「フロー演算子」とよばれる演算子として利用できます。
フロー部品の中にフロー演算子を含めることもでき、
複雑なデータフローを階層化して取り扱えます。

ジョブフローに対して、フロー部品は次のような特徴があります。

外部入出力を定義しない
    フロー部品単体では外部入出力を定義できず、
    かならずいずれかのジョブフローの中で利用されることになります。
    このため、ジョブフローで指定したインポートやエクスポートの指定は不要です。
フロー演算子を自動生成する
    `Operator DSLコンパイラ`_ を利用すると、フロー部品に対応する
    フロー演算子を自動的に生成します。
値引数を利用できる
    フロー部品には入出力以外に任意の引数を指定できます。
    一部の値のみが異なる複数のデータフローをフロー部品として抽出すると、
    データフローの再利用性が高まります。
型引数を利用できる
    フロー部品は :doc:`generic-dataflow_ja` に対応しています。
    データフロー内で利用するデータモデルの種類を型引数として宣言でき、
    内部では多相化した演算子を利用できます。

..  note::
    フロー部品はデータフローの構造化と再利用を意識して導入した仕組みです。
    またフロー部品は単体テストの単位ともなるので、意味のある単位で構成することで
    データフローのテストが容易になります。

フロー部品クラス
~~~~~~~~~~~~~~~~
それぞれのジョブフローは、 `ジョブフロー`_ と同様に
``FlowDescription`` [#]_ を継承したJavaのクラスとして宣言します。
このクラスには以下のような制約があります。

* ``public`` として宣言されている
* ``abstract`` として宣言されていない
* ``FlowDescription`` を継承する
* 注釈 ``FlowPart`` [#]_ を付与する
* 明示的なコンストラクターを一つだけ宣言する

..  note::
    フロー部品クラスはジョブフロークラスと異なり、型引数の宣言が可能です。
    詳しくは :doc:`generic-dataflow_ja` を参照してください。

以下はフロー部品クラスの例です。

..  code-block:: java

    package com.example.business.flowpart;

    import com.asakusafw.vocabulary.flow.*;

    @FlowPart
    public class StockPart extends FlowDescription {

    }

..  [#] ``com.asakusafw.vocabulary.flow.FlowDescription``
..  [#] ``com.asakusafw.vocabulary.flow.FlowPart``


フロー部品コンストラクタ
~~~~~~~~~~~~~~~~~~~~~~~~
フロー部品の入出力は、ジョブフローと同様にコンストラクタで宣言します。
これには次のような制約があります。

* ``public`` として宣言されている
* 型引数を宣言していない
* ``In`` [#]_ 型の仮引数を一つ以上宣言し、それぞれ型引数にデータモデル型または型変数を指定する
* ``Out`` [#]_ 型の仮引数を一つ以上宣言し、それぞれ型引数にデータモデル型または型変数を指定する

それぞれの ``In`` 型の引数は、フロー部品への1つ分の入力を表しています。
同様に、それぞれの ``Out`` 型の引数は、フロー部品からの1つ分の出力を表しています。

..  attention::
    フロー部品のコンストラクタには、入出力以外にも任意の引数を利用できます。

以下はフロー部品コンストラクタの例です。

..  code-block:: java

    In<Shipment> shipmentIn;
    In<Stock> stockIn;
    Out<Shipment> shipmentOut;
    Out<Stock> stockOut;

    /**
     * コンストラクタ。
     * @param shipmentIn 処理対象の注文情報
     * @param stockIn 処理対象の在庫情報
     * @param shipmentOut 処理結果の注文情報
     * @param stockOut 処理結果の在庫情報
     */
    public StockPart(
            In<Shipment> shipmentIn,
            In<Stock> stockIn,
            Out<Shipment> shipmentOut,
            Out<Stock> stockOut) {
        this.shipmentIn = shipmentIn;
        this.stockIn = stockIn;
        this.shipmentOut = shipmentOut;
        this.stockOut = stockOut;
    }

..  [#] ``com.asakusafw.vocabulary.flow.In``
..  [#] ``com.asakusafw.vocabulary.flow.Out``

フロー部品のフロー記述
~~~~~~~~~~~~~~~~~~~~~~
フロー部品のフロー記述は、ジョブフローと同様です。
`フロー記述メソッド`_ を参照してください。


データフローのコンパイル
------------------------
Asakusa Frameworkでは、通常Flow DSLのプログラムを直接コンパイルしません。
これらはバッチに含めた状態でコンパイルされます。
詳しくは `Batch DSLコンパイラ`_ を参照してください。

なお、フロー部品を `Operator DSLコンパイラ`_ に掛けると「フロー演算子」を作成します。
これはジョブフローやフロー部品に、他のフロー部品を組み込むための演算子です。
フロー演算子については、 :doc:`operators_ja` を参照してください。


Batch DSL
=========
Batch DSLはデータフローを組み合わせて複雑なバッチ処理の流れを記述するDSLです。
それぞれのデータフローを処理する順序を、依存関係のグラフ構造で記述できます。

バッチ
------
バッチはBatch DSLに出現する唯一の要素で、
「エンドユーザーから見たバッチ処理の単位」を表すことを想定しています。
`ジョブフロー`_ は外部システムからの入力を取り込んで、
処理結果を出力するまでの一連の流れを表しています。
バッチはそれらをさらに組み合わせて、意味のある一連の処理を記述できます。

Batch DSLで記述する内容は、主に「ジョブフローの実行順序」です。
それぞれのジョブフローの実行順序を、ジョブフロー間の依存関係を元に記述します。
依存関係のあるジョブフローは、手前のジョブフローの処理が完了するまでブロックされ、
それらがすべて終了したのちにジョブフローの処理が開始されます。

..  note::
    Batch DSLではデータフロー以外の処理を連携できるようにする計画があります。
    たとえば、外部システムからデータを取り込むようなスクリプトを
    後続のデータフロー処理に先立って起動するなどです。

バッチクラス
~~~~~~~~~~~~
それぞれのバッチは、バッチクラスのベースクラスである
``BatchDescription`` [#]_ を継承したJavaのクラスとして宣言します。
このクラスには以下のような制約があります。

* ``public`` として宣言されている
* ``abstract`` として宣言されていない
* ``BatchDescription`` を継承する
* 注釈 ``Batch`` [#]_ を付与する
* 型引数を宣言していない
* 明示的なコンストラクタを宣言しない

また、注釈 ``Batch`` の要素 ``name`` にこのバッチの名前を指定します。
ここで指定する名前は、 Javaのパッケージ名のうち、ASCIIコード表に収まるもののみでなければなりません。

以下はバッチクラスを作成する例です。

..  code-block:: java

    package com.example.batch;

    import com.asakusafw.vocabulary.flow.*;

    @Batch(name = "example")
    public class ExampleBatch extends BatchDescription {

    }

..  [#] ``com.asakusafw.vocabulary.batch.BatchDescription``
..  [#] ``com.asakusafw.vocabulary.batch.Batch``


バッチ記述メソッド
~~~~~~~~~~~~~~~~~~
バッチの内容は、 ``BatchDescription`` クラスの ``describe`` メソッドをオーバーライドして記述します。
このメソッドの中には、ジョブフローの依存関係を記述してバッチ全体を構築するようなプログラムを書きます。
以下はバッチメソッドを記述する例です。

..  code-block:: java

    @Override
    protected void describe() {
        Work first = run(FirstFlow.class).soon();
        Work second = run(SecondFlow.class).after(first);
        Work para = run(ParallelFlow.class).after(first);
        Work join = run(JoinFlow.class).after(second, para);
        ...
    }

バッチの内部で実行するジョブフローは、 ``BatchDescription`` クラスから継承した ``run()`` メソッドで指定します。
同メソッドには対象のジョブフロークラスのクラスリテラルを指定し、
そのままメソッドチェインで ``soon()`` や ``after()`` メソッドを起動します。

``soon`` メソッドはバッチの内部で最初に実行されるジョブフローを表し、
``after`` メソッドは依存関係にある処理を引数に指定して、
それらの処理が全て完了後に実行されるジョブフローを表します。

..  caution::
    このメソッドは、 `Batch DSLコンパイラ`_ の *コンパイル中に* 起動されます。
    そのため、 `フレームワークAPI`_ はこの中では利用できません。

Batch DSLコンパイラ
-------------------
Batch DSLコンパイラは、バッチクラスから次のものを生成します。

* 外部入出力を行うための設定情報など
* データフロー処理を行うMap Reduceプログラム群
* 上記の一連の流れを規定する `ワークフロー記述`_

コマンドラインインターフェース
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
単一のバッチクラスをコンパイルする場合には、
``BatchCompilerDriver`` [#]_ クラスを実行します。
以下の引数を指定します。

``-class <クラス名>``
    コンパイル対象のバッチクラス名。
    ``com.example.Hoge`` のように完全限定名で指定する。
``-output <ローカルパス>``
    コンパイル結果を出力する先のディレクトリ。
    存在しないパスを指定した場合には自動的にディレクトリを作成する。
    ここに指定したパスは実行前にクリアされる可能性がある。
``-compilerwork <ローカルパス>``
    コンパイラのワーキングディレクトリ。
    存在しないパスを指定した場合には自動的にディレクトリを作成する。
    ここに指定したパスは実行前にクリアされる可能性がある。
``-hadoopwork <DFSの相対パス>``
    Hadoop上でのワーキングディレクトリ (ホームディレクトリからの相対パス)。
    このパス以下にジョブフローの中間データを作成し、終了後に削除する場合がある。
    現在の実装では、プロトコル名を指定できない。
``-package <パッケージ名>``
    コンパイル結果のベースパッケージ
``-link <クラスパス>`` (省略可)
    リンクするクラスライブラリの一覧。
    ここに指定したクラスライブラリは、バッチをコンパイルした結果のクラスライブラリにマージされる。
    パス区切り文字で区切って複数指定可能。
``-plugin <クラスパス>`` (省略可)
    利用するコンパイラプラグインの一覧。
    ここに指定するか、または単に実行時のクラスパスに指定すればコンパイラプラグインを利用できる。
    パス区切り文字で区切って複数指定可能。

なお、 ``-hadoopwork`` で指定するパスには、パス変数を含めることもできます。
パス変数は ``${変数名}`` の形式で指定し、バッチ起動時の引数や、あらかじめ宣言された変数を利用できます。
利用可能な変数は、 `コンテキストAPI`_ で参照できるものと同様です。

..  attention::
    現在の仕様では、 ``-hadoopwork`` で指定したパスの下に、
    ジョブフローの実行ごとにユニークなディレクトリを生成して
    そこにジョブフローの中間データを出力します。
    パスにバッチIDやフローIDを含めておくことで、障害時の追跡が多少楽になる可能性があります。

ディレクトリに含まれるすべてのバッチクラスをコンパイルする場合は、
``AllBatchCompilerDriver`` [#]_ クラスを実行します。
このプログラムに指定可能な引数のうち、以下は ``BatchCompilerDriver`` の引数と同様です。

* ``-output``
* ``-compilerwork``
* ``-hadoopwork``
* ``-package``
* ``-link``
* ``-plugin``

``-scanpath <クラスパス>``
    コンパイル対象のバッチを含むクラスライブラリ。
    ここに含まれるクラスのうち、 `バッチクラス`_ として適格なもののみがコンパイルされる。
``-skiperror`` (省略可)
    指定された場合、コンパイルエラーが発生しても続けて次のバッチをコンパイルする。
    指定がない場合は、コンパイルエラーを見つけた時点でコンパイルを中断する。

..  [#] ``com.asakusafw.compiler.bootstrap.BatchCompilerDriver``
..  [#] ``com.asakusafw.compiler.bootstrap.AllBatchCompilerDriver``

ワークフロー記述
~~~~~~~~~~~~~~~~
ワークフロー記述は、コンパイルされたバッチを実行する際に
入出力やMap Reduceジョブの実行順序を記述したものです。
これはワークフローエンジンごとに生成される記述で、
対応するコンパイラプラグインをコンパイル時に指定します。

標準では、Experimental Shell Scriptという、各ジョブを直列化して
順に実行するだけのbashのスクリプトを生成しています。
Experimental Shell Scriptについては、 :doc:`../application/start-guide_ja` などを参照してください。

{'fraction_non_alphanumeric': 0.13054450575302623, 'fraction_numerical': 0.002566405748748877, 'mean_word_length': 4.833333333333333, 'pattern_counts': {'":': 0, '<': 32, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5265600', 'n_tokens_mistral': 16858, 'n_tokens_neox': 16058, 'n_words': 1247}