S-JIS[2011-08-21/2011-09-16] 変更履歴

AfwHS α版

Asakusa FrameworkScalaで記述するライブラリー「AfwHS」α版。


概要

AfwHSは、Scalaで記述したコードをAsakusaFWの(Java)DSLに変換して実行できるようにするライブラリー。

AfwHSはコードネーム「AsakusaFramework Hishidama Scala」の略かなぁ(苦笑)
「Hishidama」なんて恥ずかしい単語が入っている理由は、AsakusaFWでScalaと言えばasami224さんの「Asakusa Scala DSL」の事を指すので、違いを出す必要があるから…。
「AfwHS」を何て発音すればいいか分からないから(爆)、「AHS」でいいかも。

DMDLで作ったJavaのモデルクラスを元に、AsakusaFWの演算子(アノテーション)を模したScalaの関数(これがAfwHSで用意されているもの)を使って処理を記述する。
それを元にAsakusaFWのOperator・JobFlowを生成する。

とりあえず作ってみたって感じなので、α版。


WordCountの例

AfwHSでWordCountを書くと、こんな感じ。

import scala.collection.JavaConverters._

import jp.hishidama.afwhs._

import sample.wordcount.modelgen.dmdl.model.LineModel
import sample.wordcount.modelgen.dmdl.model.LineModelWrapper
import sample.wordcount.modelgen.dmdl.model.LineModelImplicit._
import sample.wordcount.modelgen.dmdl.model.WordCountModel
import sample.wordcount.modelgen.dmdl.model.WordCountModelWrapper
import sample.wordcount.modelgen.dmdl.model.WordCountModelImplicit._
object WordCount {
  def run(in: Source[LineModel]) = {
    val split = in.cogroup("split", "", Result[WordCountModel]("out")) { (s, r) =>
      s.asScala.foreach {
        _.getTextAsString().split("[ \t]+").foreach { word =>
          r.add(WordCountModelWrapper(word, 1))
        }
      }
    }
    val count = split.cogroup("sum", "word", Result[WordCountModel]("out")) { (s, r) =>
      val word  = s.get(0).getWordAsString()
      val count = s.asScala.map(_.getCount).sum
      r.add(WordCountModelWrapper(word, count))
    }

    count //戻り値
  }

青い字はDMDLによって生成されたモデルクラスやそのメソッドそのもの。
オレンジ色のはモデルのScala用ラッパークラス。これもモデルドライバーの機能で生成される。

Source/ResultやcogoupはAsakusaFWのクラス(DSL)を模したもので、AfwHSで用意しているクラスやメソッド。

s0はデータモデルのjava.util.Listで、CoGroupの引数と全く同じ。
r0も同じくCoGroupで戻り値を返すResultそのもの。

その他は普通のScalaのメソッド・関数。


このrunメソッドを呼び出す方法が2種類ある。

  def main(args: Array[String]) {
    if (!args.exists(_ == "generate")) {

      // 普通にデータを渡して実行する
      val slist = List("Hello World", "Hello Asakusa", "Hello Hadoop")
      val mlist = slist.map { text => LineModelWrapper(text).asJava }
      val r = run(Source(mlist))
      println(r)

    } else {
      // Asakusa DSLを生成する
      val gen = new Generator("C:/workspace", "afwhs-wordcount", "sample.wordcount", "WordCount")
      val r = run(gen.newSource())
      gen.out(r)

      gen.prepareGenerate()
      gen.generateOperator()
      gen.generateImporter()
      gen.generateExporter()
      gen.generateFlow()
      gen.generateFlowTest()
    }
  }
}

とりあえず、実行時の引数に「generate」という文字列が入っているかどうかで処理を分岐している。

generate
以外
Sourceに入力データ(データモデルのリスト)を渡すと、そのデータを使って実際に処理を行って結果のデータ(データモデルのリスト)を返す。
((AsakusaFWのデータモデルを使っているという以外は)AsakusaFWやHadoopとは無関係に単なるScalaのプログラムとして実行する)

Source++List({class=word_count_model, word=World, count=1}, {class=word_count_model, word=Asakusa, count=1}, {class=word_count_model, word=Hello, count=3}, {class=word_count_model, word=Hadoop, count=1})

generate SourceにGeneratorから作ったオブジェクトを渡すと、Asakusa DSL生成用の情報収集を行う。
generateXxx()でAsakusa DSLの生成を行う。

もうちょっと詳しいWordCountの例


変更履歴

α0.001 とりあえずイメージを作ってみた。 [/2011-08-23]
α0.002 ソースの自動生成を作ってみた。実行は途中までしか出来ない。 [/2011-08-22]
α0.003 α0.002の実行が途中までしか出来ない問題点をどうするか考えた。 [2011-08-23]
α0.004 DMDLを使い、ラッパークラスを生成することにした。 [2011-08-23]
  とりあえず最小限は動かせる状態になった。 [2011-08-28]
  JobFlowのテストクラスの生成処理を追加。 [2011-08-31]
  prepareGenerate()を新設。fold演算子を追加。 [2011-09-04]
  afwhs.javaパッケージをafwhs.forjavaに変更。 [2011-09-16]

AsakusaFWへ戻る / 技術メモへ戻る
メールの送信先:ひしだま