Scala 実践編(2) biz-Streamへデータを流し込む

前回予告していました,PDFへのデータの流し込みの例をScalaを使用して紹介したいと思います.
具体的にはOAuthで認証後,Twitterのデータを流しこんでみようと思います.有名な話ですがTwitterのサービス自身もScalaを利用して構築されてたりします.

まずは,下の実装例の大まかな説明をします.

一番始めの部分(layoutXml)は,biz-Streamのレイアウト定義を直接ソースコード内に 記述しています.
レイアウト定義は通常2種類(ドキュメントレイアウト定義,ページレイアウト定義)の別ファイルで構成されますが,ここではそれらを1つに結合しています.

次にnodeSeqToDocument()は上記のXMLレイアウトをbiz-Streamに渡すため,JavaのDOMオブジェクトに変換しています.
launchWebBrowser()は個々人のTwitter内の情報を参照するためにOAuthをつかって,認証をするためにWebブラウザを起動させるためのものです.

main()では,実行時の引数からTwitterサービス用のコンシューマキーとシークレット,そして出力するPDFのファイル名を取得しています.
(Twitterサービス用のコンシューマキーとシークレットは,Twitterサイトで別途取得しておく必要があります)

その後,OAuthによるTwitterサイトでの認証をWebブラウザ上で行い,暗証番号を本アプリに入力する.
するとTwitterからデータを取り込むことができるようになるので,biz-Streamにデータを渡すためのオブジェクトRecordTypeへTwitterサイトから取得したデータを設定します.
そして,用意したデータをbiz-Streamへ渡しPDFを生成します.

今回は,biz-StreamのJava APIとScala以外に,dispatchというScala用のライブラリを利用しています.

今回は、依存するライブラリもあるため、sbtにて簡単にビルド、実行するためのファイルを用意しました。
実行方法は、展開後、README.txtをご覧ください。
biz-scala2.zip

出力例は次のとおりです。

作成したPDF

《 Scalaからbiz-Streamへデータを流し込んで作成したPDF 》

ソースコードは次のようになります。

《 Scalaからデータを流し込みbiz-StreamによりPDFを生成するソースコード 》
package twitting

import xml.NodeSeq
import dispatch._
import dispatch.oauth._
import dispatch.twitter._
import java.io.StringReader
import java.net.URI
import javax.xml.parsers.DocumentBuilderFactory
import org.xml.sax.InputSource
import com.brainsellers.xml.JaxpXML
import com.brainsellers.xml.datatypes.{HashtableType, RecordType}

object run {
  val DATASOURCE_NAME = "app-resource"
  val SCREEN_NAME = "screenName"
  val FOLLOWERS_COUNT = "followersCount"
  val TWITTE = "twiite"

  val layoutXml =
    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:pdf="http://www.brainsellers.com/schema" xmlns:svg="http://www.brainsellers.com/schema" xmlns:bs="http://www.brainsellers.com/schema" xmlns:form="http://www.brainsellers.com/schema">
      <bs:datasource-master-set>
        <bs:application-data-resource-master master-name={DATASOURCE_NAME}/>
        <bs:application-data-master master-name="app-field">
          <bs:application-data-statement master-reference={DATASOURCE_NAME}/>
        </bs:application-data-master>
        <bs:datasource-master master-name="app-source" position="inherit">
          <bs:application-data-master-reference master-reference="app-field"/>
        </bs:datasource-master>
      </bs:datasource-master-set>
      <fo:layout-master-set>
        <fo:simple-page-master master-name="A4" page-height="297mm" page-width="210mm">
          <fo:region-body>
          <fo:region-before extent="0">
          <fo:region-after extent="0">
          <fo:region-start extent="0">
          <fo:region-end extent="0">
        </fo:region-end></fo:region-start></fo:region-after></fo:region-before></fo:region-body></fo:simple-page-master>
      </fo:layout-master-set>
      <fo:page-sequence master-reference="A4">
        <fo:flow flow-name="xsl-region-body">
          <bs:block-container>
            <!-- ページレイアウト定義開始 -->
            <Layout width="210mm" height="297mm" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:bs="http://www.brainsellers.com/schema" xmlns:svg="http://www.brainsellers.com/schema" xmlns:form="http://www.brainsellers.com/schema" xmlns:pdf="http://www.brainsellers.com/schema">
              <flow-area name="flow-area" x="14" y="280" width="180" height="260" master-reference="app-source" no-data-disabled="false">
                <flow-table line-height="10" record-type="header" view="page" border-color="0,255,255">
                  <flow-table-cell cell-width="60" align="horizon" text-align="left" cell-align="bottom" narrow="size">
                    <Label Width="60" Height="10" Horizon="center" Vertical="center" Vector="horizon" FontSize="12" FontStyle="BOLD" Narrow="horizon">名前</Label>
                  </flow-table-cell>
                  <flow-table-cell cell-width="30" align="horizon" text-align="left" cell-align="bottom" narrow="size">
                    <Label Width="30" Height="10" Horizon="center" Vertical="center" Vector="horizon" FontSize="12" FontStyle="BOLD" Narrow="horizon">フォロワー数</Label>
                  </flow-table-cell>
                  <flow-table-cell cell-width="90" align="horizon" text-align="left" cell-align="bottom" narrow="size">
                    <Label Width="90" Height="10" Horizon="center" Vertical="center" Vector="horizon" FontSize="12" FontStyle="BOLD" Narrow="horizon">ツイート</Label>
                  </flow-table-cell>
                </flow-table>
                <flow-table record-type="details" view="layout">
                  <flow-table-cell cell-width="60" border-color="0,255,255" cell-align="top" narrow="size">
                    <multi-text name={SCREEN_NAME} cell-width="60" font-size="11" margin="start:1;end:1;top:1;bottom:1"/>
                  </flow-table-cell>
                  <flow-table-cell cell-width="30" border-color="0,255,255" cell-align="top" narrow="size">
                    <multi-text name={FOLLOWERS_COUNT} cell-width="30" font-size="11" margin="start:1;end:1;top:1;bottom:1"/>
                  </flow-table-cell>
                  <flow-table-cell cell-width="90" border-color="0,255,255" cell-align="top" narrow="size">
                    <multi-text name={TWITTE} cell-width="90" font-size="11" margin="start:1;end:1;top:1;bottom:1"/>
                  </flow-table-cell>
                </flow-table>
              </flow-area>
            </Layout>
            <!-- ページレイアウト定義完了 -->
          </bs:block-container>
        </fo:flow>
      </fo:page-sequence>
    </fo:root>

  // ScalaのXMLオブジェクトからJavaのDOMオブジェクトに変換
  def nodeSeqToDocument(xml: NodeSeq) =
    DocumentBuilderFactory.newInstance.newDocumentBuilder.parse(
      new InputSource(new StringReader(xml.toString)))

  // Webブラウザを起動
  def launchWebBrowser(url: URI) {
    val desktop = Class.forName("java.awt.Desktop")
    desktop.getMethod("browse", classOf[java.net.URI]).invoke(
      desktop.getMethod("getDesktop").invoke(null), url)
  }

  def main(args: Array[String]) {
    val CONSUMER_KEY = args(0)
    val CONSUMER_SECRET = args(1)
    val CONSUMER = Consumer(CONSUMER_KEY, CONSUMER_SECRET)
    val OUTPUT_PDF = args(2)

    // OAuthによるTwitterサイトでの認証
    val http = new Http
    val reqToken = http(Auth.request_token(CONSUMER))
    val authorizeUrl = Auth.authorize_url(reqToken).to_uri
    launchWebBrowser(authorizeUrl)
    print("暗証番号は?")
    var pin = readLine

    // 流し込むデータの準備
    val records = new RecordType
    val (accessToken: Token, userId: String, screenName: String) =
    http(Auth.access_token(CONSUMER, reqToken, pin))
    val tweets = http(Status.friends_timeline(CONSUMER, accessToken).product)
    tweets.reverse foreach {
      js =>
        val Status.user.screen_name(screenName) = js
        val Status.text(text) = js
        val Status.user.followers_count(followersCount) = js
        val record = new HashtableType
        record.put(SCREEN_NAME, screenName)
        record.put(FOLLOWERS_COUNT, followersCount.toString)
        record.put(TWITTE, Status.rebracket(text))
      records.add(record)
    }

    // PDFの生成
    val bizLib = new JaxpXML(nodeSeqToDocument(layoutXml), OUTPUT_PDF)
    bizLib.parse
    bizLib.setDataSource(DATASOURCE_NAME, records)
    bizLib.calcDataSize
    bizLib.toPDF
    bizLib.close
  }
}
biz-Stream詳細情報  biz-Stream資料請求

超高速!!高機能!! Web帳票ソリューション biz-Stream

オンデマンドかつリアルタイムにビジネスドキュメントを生成する帳票ソリューション