canvas+herokuでsaleseforce外のデータを表示してみた

最近、salesforce外にあるDBのデータをsalesforce上に表示したい、もしくはsalesforceの一部のデータを外部のDBに退避したいが、退避したデータは時々salesforceからも参照したい、というような話をちらほら聞くようになってきました。

単純に外部のデータをsalesforce上で参照したいということもあるでしょうし、salesforceの定着化が進みsalesforceのデータを別システムで蓄積しているデータと突き合わせて分析等に活用できそうなシーンが増えてきたのかもしれません。 もしくは、salesforceの利用期間が長くなってきたため、大量のデータがsalesforceに蓄積されてきて利用データ量の上限に達しそうなのでどうにかしたいというユーザさんも増えてきたのかもしれませんね。 もしかすると同様のニーズは今後高まっていくのかもしれません。

前置きが長くなりました。今回は特に目新しい内容ではありませんが、salesforce外に配置したRDBに保持しているデータをsalesforceのcanvas上に表示するところまで実装してみましたので、そのお話しをしようと思います。

ちなみにODataも気にはなったのですが、salesforceではまだサポートされていないようなので試しようがなかったのと、またheroku connectの事もふと頭をよぎりましたがデータの同期とは違う話なので、今回はherokuの勉強も兼ねて下記のような構成で実装してみました。

概要

取引先の詳細画面に埋め込まれたcanvas上に、PostgreSQL(herokuのアドオン)に格納されているデータを表示するというものです。 概要(heroku、canvas)一つずつ説明していきます。

実装

①visualforceの呼び出し

canvasを使用するシンプルなvisualforceです。 developerName属性には作成した接続アプリケーションのAPI参照名を指定します。 parameters属性にはJSON形式、またはJavaScriptオブジェクトリテラルを指定します。parameters属性に指定した値はherokuのアプリに送信されます。今回は表示された取引先のIdをパラメータとして渡しています。

②canvasからherokuにリクエスト

canvasからherokuへのアクセス方法ですが、今回はOAuthではなくシンプルな署名付き要求(SignedRequest)を採用しております。 署名付き要求(SignedRequest)の実装については、salesforce社の中嶋さんのブログを参考にしておりますので、詳しくはそちらをご参照ください。

③SignedRequestをチェックしてDBからデータを取得

canvasからPOSTされてきたデータが正しいものか、途中経路で改ざんされていたり偽装されていないかチェックします。 チェック方法は、データと一緒に送られてきた署名と、herokuで生成した署名が一致すればDBからデータを取得してレスポンスを返します。

herokuがリクエストを受けてからレスポンスを返すまでの流れをプログラムと共に簡単に説明します。 まず今回heroku上に構築したアプリはplay framework(version2.2.2)を使用してJavaで実装しています。 ちなみにplay frameworkはサーバサイドJavaとScalaのためのMVCフレームワークだそうです(詳細はこちらをご参照ください)

下記がPOSTされてきたデータに含まれる署名をチェックする処理に関連する部分です。

playではactionが呼ばれる前にリクエストに対して一律で何かの処理を行う際に使用する方法が提供されていて、GlobalSettingsクラスを継承したクラスを作成しルートパッケージに配置することで機能します(今回はGlobalクラスがそれにあたります。サーバーサイドJavaに触れた事のある方にはServletFilterのようなものと言うと馴染み深いかもしれません)

全リクエスト(cssやjavascript等のいわゆる静的なリソースに対するリクエスト以外)に対してGlobalクラスから呼び出すSignedRequestクラスで署名のチェックを行います。 POSTされたデータとsalesforceで設定されている秘密鍵を用い同様のアルゴリズムで署名を生成し、POSTされた署名と一致すれば正常なリクエストとみなしてroutesでマッピングしているアクションが呼び出されるようにしています。 もし署名が相違する場合は不正なアクセスとみなし何も処理を行わずに空のレスポンスを返却します。

次にアクションです。

routesでマッピングしているアクションindexでは、canvasのparameter属性で指定したパラメータの取引先Idを取得して、PostgreSQLのaccountテーブルに対してplayに標準で実装されているORM(Ebean)で検索を実行します。 そして検索結果をjson形式に変換してビューに渡します。

playではビューを作成する場合に使用するテンプレートエンジンが用意されています。 最低限の文法さえ押さえてしまえば、それほど難しいものではないかなという印象です。 今回はアクションで生成したjson形式のデータをjavascriptの変数に設定する部分だけテンプレートエンジンを意識する形で実装しています。

実行

それでは実際に、canvasを含んだvisualforceが埋め込まれている取引先の詳細画面を表示してみます。 取引先画面2

取引先の詳細画面にherokuのPostgreSQLのデータが一覧表示されました。 ちなみにこの一覧画面では取引先のIDで絞ったデータのみを表示するようにしています。

おまけ

上の概要図のAWSの部分にRDSをおまけとして置いておきました。 実装が完了した後、ふと接続先のDBをherokuのPostgreSQLからRDSのMySQLに変更したくなってきたのでやってみたいと思います。

RDSのインスタンスを北米リージョンで新規に作成し、Security GroupでherokuのインスタンスのIPアドレスからの接続を許可するように設定します。 herokuのプログラムは変更しないでそのままですが、MySQLのJDBCドライバをプロジェクトに配置して再度herokuへpushします。 PostgreSQLではなくちゃんとMySQLにアクセスしている事が見た目で分かるように、MySQLにはデータを5件のみ登録します。 取引先画面大して複雑なクエリでもありませんし、ORM(EBean)を利用してJDBCで接続しているので、特に問題にはなりませんね。 サンプルとしては以上になりますが、時間の関係上いろいろとやり残したことや気になることがあります。

やり残したことや気になること

  • テストクラスの作成

今回はコントローラや署名をチェックするクラスを作成しましたのでそれらのテストクラスを作成するのは当然ですが、play frameworkではビュー(上でお見せしたindex.scala.html)のテストも行えるようです。ビューから生成されたhtmlの内容をチェックする感じですね また、テストクラスから起動したplay frameworkが提供しているテスト用のWEBサーバに対して、Selenium WebDriverを使用して実際にリクエストを送信してレスポンスの内容をチェックする事が可能だそうです。面白いですね。

  • 署名のチェック方法の改善

heroku側ではリクエストの度に署名のチェックをするようになっているので、一度署名のチェックが済んだらplay frameworkのセッションスコープに署名のチェックが済んでいることを表す値を保持して、2回目以降のリクエスト時には署名が不要になるように実装を変えたいです。play frameworkのセッションデータはサーバ側で保持するのではなく暗号化した上でクッキーに保持するので、有効期限を表す値を持たせて、半永久的にアクセスできる状態になるのは防ぎたいです。

  • 検索画面の作成

今回は特定の条件に基づく結果を参照するだけでしたが、heroku側で検索画面を表示し、ユーザが条件を細かく指定して検索できるような検索画面を作成したかったです。この場合上で記述した署名のチェック方法の改善されていることが前提となりますが。

  • セキュリティまわり、もっと意識しないとな・・・
  • 複雑なクエリ書く場合、EBeanではどう実装するんだろう?
  • DBコネクションのプーリングの管理にBoneCPというものが使用されているらしいけどまだ理解していない・・・
  • herokuの制限とか仕様とかまだよく理解していないな・・・

最後に

herokuとplay frameworkに関して、名前は聞いていたのですがほとんど触ったことがなかったので最初いろいろ躓きましたが、でも慣れてくるにつれて段々と開発が楽しくなってきたのがとても新鮮でした。

特にherokuに対してのデプロイがとても簡単で、gitでherokuにpushするだけで最新の状態に反映できる仕組みが最初から備わっているのはとても楽で、開発に集中できるように良く考えられているなと感じました。 限られた時間の中での開発だったので浅い実践となりましたが、こちらのワークブックの内容を試したり、本でも買ってもっと深く実践していこうと思います。