Scala 実践編(4) biz-Stream Webサービスサーバを使った例<後編>

今回は,前回作成したSOAPメッセージを実際にbiz-Stream Webサービスサーバに送ってみたいと思います.

前回の「Scala 実践編(3) biz-Stream Webサービスサーバを使った例<前編>」は,Webサービスサーバへ送信するためのSOAPメッセージを構築するところまで説明しました.
その際に,指定したファイル名でPDFファイルをWebサービスサーバ上のコンテンツリポジトリへ出力するように指示していましたが,せっかくなので今回はWebサービスサーバのクライアント側にPDFファイルデータを返す例にSOAPメッセージ部分を変更しました.

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

作成したPDF

《 Scalaからbiz-Streamへデータを流し込んで作成したPDF(Webサービスサーバ版) 》

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

《 Scalaからbiz-Stream Webサービスサーバへデータを送りPDFを生成するソースコード(完全版) 》
package wssclient

import dispatch.{:/, Http}
import java.io._

object wss_client {
  val FILE_NAME = "output_test.pdf"

  val DATASOURCE_NAME = "app-resource"
  val COL1 = "col1"
  val COL2 = "col2"
  val COL3 = "col3"

  val RESOURCE_NAME = "resource1"

  def main(args: Array[String]) {
    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">col1</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">col2</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">col3</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={COL1} 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={COL2} 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={COL3} 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>

    val layoutString = layoutXml.toString

    val records = List(
      Map(1 -> "data1", 2 -> "data2", 3 -> "data3"),
      Map(1 -> "data2_1", 2 -> "data2_2", 3 -> "data2_3"),
      Map(1 -> "data3_1", 2 -> "data3_2", 3 -> "data3_3"))

    val soapMessage =
    <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns="http://schemas.brainsellers.com/webservices/bizstream/2006-10" xmlns:ns1="http://schemas.brainsellers.com/webservices/common/2006-10" xmlns:xm="http://www.w3.org/2005/05/xmlmime">
        <soap:Header/>
      <soap:Body>
        <generateDataRequest
        xmlns="http://schemas.brainsellers.com/webservices/bizstream/2006-10"
        xmlns:common="http://schemas.brainsellers.com/webservices/common/2006-10">
          <dataType>
            <pdf/>
          </dataType>
          <resource>
            <resourceData name={RESOURCE_NAME}>
              <applicationData masterName={DATASOURCE_NAME}>
                <recordData>
                  <header>
                      <headerColumn value={COL1}/>
                      <headerColumn value={COL2}/>
                      <headerColumn value={COL3}/>
                  </header>{records.map(record =>
                  <detail>
                      <detailColumn value={record(1)}/>
                      <detailColumn value={record(2)}/>
                      <detailColumn value={record(3)}/>
                  </detail>)}
                </recordData>
              </applicationData>
            </resourceData>
          </resource>
          <layoutData>
            <layoutDefinition dataReference={RESOURCE_NAME}>
              {layoutString}
            </layoutDefinition>
          </layoutData>
        </generateDataRequest>
      </soap:Body>
    </soap:Envelope>

    val tempStream = new ByteArrayOutputStream
    val http = new Http
    val url = :/("wss_server_host_name", 8080) / "axis2/services/bizstream.BizstreamSOAP12port/"
    val authed = url as ("bizuser1", "bizuser1")
    val req = authed <:< Map("content-type" -> """application/soap+xml;charset=utf-8;action="urn:generateData"""")
    val soapReq = req << soapMessage.toString
    http(soapReq >>> tempStream)
    extractPdfFile(tempStream.toByteArray, FILE_NAME)
 }
  
 def extractPdfFile(bytes : Array[Byte], outputFileName : String) = {
    val outputFile = new FileOutputStream(outputFileName)
    val START = "%PDF"
    val END = "%%EOF"
    var foundStart = false
    var foundEnd = false
    var i = 0
    for (i <- 0 to bytes.length - 1 if !foundEnd) {
        if (!foundStart) {
            var notFound = false
            for (j <- 0 to START.length - 1 if !notFound) {
                if (bytes(i+j) != START(j))
                    notFound = true
                else if (j == START.length - 1) {
                    foundStart = true
                    outputFile.write(bytes(i))
                }
            }
        } else {
            var notFound = false
            outputFile.write(bytes(i))
            for (j <- 0 to END.length - 1 if !notFound) {
                if (bytes(i+j) != END(j))
                    notFound = true
                else if (j == END.length - 1) {
                    foundEnd = true
                    for (k <- 1 to END.length - 1)
                        outputFile.write(bytes(i+k))
                }
            }
        }
    }
    outputFile.close
 }
}

WebサービスサーバへはdispatchというScala用のライブラリを利用して,サーバにSOAPメッセージを送信します.

  • 122行: サーバから返ってくるPDFデータはひとまずtempStreamで受け取ります.
  • 123行: httpはdispatchでのHTTP通信のためのクラスです.
  • 124行: urlにてWebサービスサーバのホスト名,ポート番号,SOAPポートを指定します.
  • 125行: また,サーバへのアクセスは認証が必要なので,authedではユーザ名とパスワードを指定しています.
  • 126行: さらに,HTTPヘッダのContentTypeにSOAPメッセージであることを表す「application/soap+xml」,文字コードがUTF-8であることを表す「charset=utf-8」,SOAPのアクション(generateData)を表す「action="urn:generateData"」 を設定します.
  • 127行: soapReqでは,上記までで設定したHTTPリクエストのためのオブジェクトにSOAPメッセージを設定します.
  • 128行: そして,httpにsoapReqを渡し,HTTP送信を行った後,tempStreamにその返信内容を格納します.
  • 129行: tempStream内には,PDFファイル自体以外の情報も含まれているので,extractPdfFile()にてPDFデータのみを抽出し,ファイルに出力します.

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

biz-Stream詳細情報  biz-Stream資料請求

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

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