MuleSoft

Mule ESBを使ってデータベースへのRESTfulなWeb APIを作る

自己紹介

十川 亮平 (そごう りょうへい) Twitter: @rsogo

NCデザイン&コンサルティング株式会社で
モバイル向けのプラットフォーム (AppPot http://apppot.jp/) のプロダクトマネージャーや、モバイルやIoTの企画や技術面のコンサルタントをやってます。

Mule ESBを利用している開発者の一人としてお話しさせていただきます。


今日は

Mule ESBを使ってデータベースにアクセスするためのRESTfulなWeb APIを作るのを題材に、
Mule ESBを使った開発について話します。

内容としては、次のようなトピックを含みます。

  • HTTP Listener
  • DB Connector
  • メッセージ変換
  • エラーハンドリング

Muleとは

Mule ESBはMuleSoft社が開発したオープンソースのESB製品です。
Community Editionでも、基本的な機能やプロトコルに対応しています。

コネクタの一覧は下記のサイトから調べることができます。
https://www.mulesoft.com/exchange/#!/?types=connector&sortBy=name

Anypoint Exchange 2018-07-11 13-31-49.png

パッケージ製品用のアダプターを使いたいなどの追加の機能を使いたい場合にはEnterprise Editionが用意されています。


全体構造

全体構造

GET, POST, PUT, DELETE, GETだけれど取得条件指定など自由にかけるFreeQuery、
という5つの操作を提供するフローを作っています。今日は時間の関係で、GETとPOSTだけご説明します。

データベースのテーブルをリソースと見なして、
任意のリソースにアクセスできるようなデモです。
固定のテーブルにアクセスするだけならもっと簡単にできますが、
こんなこともできますよ、という内容になっています。


1つの操作の構造

一つの操作の構造はこのようになっています。構造のレベルではどの操作も同じです。

flow-diagram.png


RESTfulっぽくするためにやっていること

アドレス可読性

提供する情報がURIで表現されていること。
ex) http://hostname:port/emploee/115

統一インターフェイス

HTTPの一般的なやり方にならう。

HTTPメソッド

情報の取得(GET)、作成(POST)、更新(PUT)、削除(DELETE)

HTTPステータスコード

リクエストが不正(400 BadRequest)、内部的なエラー(500 InternalServerError)


HTTP Listener

フロー全体と、各操作それぞれでListenする条件を定義できます。

Listenerの構造


HTTP Listener

フロー全体のリスナー

こういうhttp://{hostname}:8085/4apppot/{任意}リクエストをリッスンする定義にしています。

<http:listener-config
 name="HTTP_Listener_Configuration"
 host="0.0.0.0" port="8085"
 basePath="4apppot"
 doc:name="HTTP Listener Configuration"/>

1つの操作用のリスナー

フロー全体のリスナーで受け取った上で、GETメソッドで、{basePath}/query/*というパスのリクエストを受け取る。

<flow name="FreeQueryFlow">
   <http:listener config-ref="HTTP_Listener_Configuration"
      path="/query/*"
      doc:name="Receive HTTP request"
      allowedMethods="GET">
     <http:response-builder
         reasonPhrase="#[flowVars['reason']]"
         statusCode="#[flowVars['statusCode']]"/>
   </http:listener>

DBコネクタ

たくさんのコネクタの中から、今回は一番使用頻度が高いかも知れないDatabase Connectorを使います。

JDBCドライバの準備

{MULE_HOME}/lib/user/以下にJDBCドライバのライブラリを入れておく

接続情報の定義

<db:generic-config doc:name="Generic Database Configuration"
     driverClassName="com.mysql.jdbc.Driver"
     name="DB_Configuration"
     url="jdbc:mysql://localhost:3306/{DATABASE_NAME}?user={DB_USER}&amp;password={PASSWORD}&amp;characterEncoding=utf8"/>

Select: GETの時

<db:select config-ref="DB_Configuration" doc:name="Perform a query in Database">
  <db:dynamic-query>
    <![CDATA[select * from #[VarTableName]
    #[VarQuery]]>]
  </db:dynamic-query>
</db:select>

Insert: POSTの時

<db:insert config-ref="DB_Configuration" doc:name="Database">
  <db:dynamic-query>
    <![CDATA[INSERT INTO #[VarTableName] (#[VarInsertColumns]) VALUES (#[VarInsertValues])]]>
  </db:dynamic-query>
</db:insert>

フロー中のメッセージの利用

Muleのフロー定義中ではMEL(Mule Expression Language)というので、メッセージの参照や計算なんかができます。

  • マニュアルからのサンプルの抜粋
#[payload]
#[message.inboundProperties.'propertyName']
#[payload.methodCall(parameters)]
#[xpath3('//root/element1')] 
#[payload.age > 21]
#[message.inboundProperties.'locale'  ==  'en_us']

Mule Expression Language Referenceに使い方の例が載っているので、困ったらここを見たら良いと思います。


JSONとの変換はとっても簡単

リクエストで受け取ったJSONメッセージをオブジェクトに変換

<json:json-to-object-transformer returnClass="java.util.ArrayList" doc:name="JSON to Object"/>

SQL実行結果のオブジェクトからJSONに変換

<json:object-to-json-transformer doc:name="Convert Object to JSON"/>

Mule ESBのエラーハンドリング

エラーハンドリングはこんな感じの書き方になります。

<flow>
    <リクエストの受取/>
    <メッセージ変換など/>
    <連携先の呼び出し/>
    <応答メッセージの編集/>
    <choice-exception-strategy>
        <例外Xが起こったときのハンドリング/>
        <例外Yが起こったときのハンドリング/>
    </choice-exception-strategy>
</flow>

実際にエラー時の応答がRESTfulっぽくなるように書いてみましょう

<flow name="GetFlow">

・・・省略・・・

    <choice-exception-strategy doc:name="Choice Exception Strategy">
        <catch-exception-strategy doc:name="Catch Exception Strategy"
            when="#[exception.causedBy(org.mule.module.db.internal.domain.connection.ConnectionCreationException)]">
            <logger level="INFO" doc:name="Log Request" message="Error processing #[flowVars['originalRequest']]" />
            <set-variable variableName="statusCode" value="500" doc:name="Set status code"/>
            <set-variable variableName="reason" value="Internal Server Error" doc:name="Set reason phrase"/>
        </catch-exception-strategy>
        <catch-exception-strategy doc:name="Catch Exception Strategy"
            when="#[exception.causedBy(org.mule.module.db.internal.resolver.query.QueryResolutionException)]">
            <logger level="INFO" doc:name="Log Request" message="Error processing #[flowVars['originalRequest']]" />
            <set-variable variableName="statusCode" value="400" doc:name="Set status code"/>
            <set-variable variableName="reason" value="Bad Request" doc:name="Set reason phrase"/>
        </catch-exception-strategy>
        <catch-exception-strategy doc:name="Catch Exception Strategy"
            when="#[exception.causedBy(java.sql.SQLSyntaxErrorException)]">
            <logger level="INFO" doc:name="Log Request" message="Error processing #[flowVars['originalRequest']]" />
            <set-variable variableName="statusCode" value="400" doc:name="Set status code"/>
            <set-variable variableName="reason" value="Bad Request" doc:name="Set reason phrase"/>
        </catch-exception-strategy>
        <catch-exception-strategy doc:name="Catch Exception Strategy"
            when="#[exception.causedBy(java.sql.SQLIntegrityConstraintViolationException)]">
            <logger level="INFO" doc:name="Log Request" message="Error processing #[flowVars['originalRequest']]" />
            <set-variable variableName="statusCode" value="400" doc:name="Set status code"/>
            <set-variable variableName="reason" value="Bad Request" doc:name="Set reason phrase"/>
        </catch-exception-strategy>
    </choice-exception-strategy>
</flow>

クスラッチ開発との比較 - Why Mule ESB

サーバーサイドエンジニアが居て、
ある特定のデータソース向けにだけにプログラムを書けば良いのであえれば、スクラッチ開発で良いかも。

ESBのメリット

  • 連携先が複数のデータソース、ソフトウェア、製品などに渡る場合
  • 自分たちがオーナーではないシステムと連携する場合(自分たちの都合でコントロールできない)

Apache Camelとの比較 - Why Mule ESB

Apache CamelはMule ESBと同様にさまざまなプロトコルに対応したインテグレーションのためのソフトウェアです。

  • Java系のソフトウェアに組み込むなら、Camelは選択肢となる
  • ただし、ESB的な用途にはそのままでは使えない
  • 独立したプラットフォームとして使いたいならMule ESB、自分たちで作るソフトウェアに組み込みたいならCamel

まとめ

今日ご説明した内容はQiitaで既に公開しています。

https://qiita.com/rsogo/items/38d3eb3dd5d2f52c432d

フローの定義の全体はGithubに公開していますので、アクセスしてみてください。
フィードバックもいただけるとありがたいです。
3.xで動作確認をしていますので、最新の4.xでは一部修正が必要です。

https://github.com/NCDCHub/mule-flows


最後に

Mule、AnyPoint Platformが日本でもっと使われて、ユーザー同士で情報交換が活発にできるようになると嬉しいです!