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

# 自己紹介
十川　亮平　（そごう　りょうへい）  Twitter: [@rsogo](https://twitter.com/rsogo)

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

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

* 2018.07.11のMuleSoft Meetup Tokyo #1用のスライドです。セッション開始まで随時更新します。
https://meetups.mulesoft.com/events/details/mulesoft-tokyo-presents-mulesoft-meetup-tokyo-1#/


***
# 今日は
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](https://www.mulesoft.com/exchange/#!/?types=connector&sortBy=name)

<img width="1560" alt="Anypoint Exchange 2018-07-11 13-31-49.png" src="https://qiita-image-store.s3.amazonaws.com/0/31063/1b8a4d55-ff7e-6569-a0e1-23c5a4551c8a.png">

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

***

# 全体構造

<img width="480" alt="全体構造" src="https://qiita-image-store.s3.amazonaws.com/0/31063/cc515e86-778e-cf71-2ac1-c722cc43d5ba.png">

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

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

***

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

![flow-diagram.png](https://qiita-image-store.s3.amazonaws.com/0/31063/904e328a-33e0-f664-ef03-d9dd0a61a7c4.png)


***

# RESTfulっぽくするためにやっていること
## アドレス可読性
提供する情報がURIで表現されていること。
ex) http://hostname:port/emploee/115

## 統一インターフェイス
HTTPの一般的なやり方にならう。
### HTTPメソッド
情報の取得（GET）、作成（POST）、更新（PUT）、削除（DELETE）

### HTTPステータスコード
リクエストが不正(400 BadRequest)、内部的なエラー(500 InternalServerError)
***

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

<img width="480" alt="Listenerの構造" src="https://qiita-image-store.s3.amazonaws.com/0/31063/84428689-516d-e0cc-2c48-ad5e7d6f3b31.png">

***

# HTTP Listener
## フロー全体のリスナー

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

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

## １つの操作用のリスナー

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

```xml
<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ドライバのライブラリを入れておく

## 接続情報の定義

```xml
<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の時

```xml
<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の時

```xml
<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](https://docs.mulesoft.com/mule-user-guide/v/3.9/mule-expression-language-reference)に使い方の例が載っているので、困ったらここを見たら良いと思います。


***

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

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

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

## SQL実行結果のオブジェクトからJSONに変換
```xml
<json:object-to-json-transformer doc:name="Convert Object to JSON"/>
```

***

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

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

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

***

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

```xml
<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/ncdcdev/mule-flows


***
# 最後に

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