前回予告していました,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
出力例は次のとおりです。
ソースコードは次のようになります。
- 《 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 } }