« 2013年11月 | メイン | 2014年1月 »

2013年12月

2013年12月27日 (金)

PapertrailのWebAPIを使う

前回記事ではHerokuのPlatformAPIを使用してリアルタイムにログを解析してグラフを作ってみたわけですが、やっぱり直近の1500行しかログが取れないとあんまりいい感じのグラフにならないわけです。(--

で、PapertrailにもWebAPIがあったことを思い出してそっちを使ってみることにしました。

 

http://help.papertrailapp.com/kb/how-it-works/http-api

 

ちなみに検索だけだけどラッパーAPIもあります。

 

http://oss.flect.co.jp/libs/ja/papertrailTool.html

 

このラッパー最初に作ったのは多分1年以上前だと思うけど、今見ると色々WebAPI進化してますね。。。

昔は検索以外のAPIなかった気がするけどいつの間にか検索条件の保存とか管理系のAPIもあるっぽい。

まぁ使うことはなさそうだけど、こうしたPaasサービスの提供する機能がなんでもAPIで叩けるようになっているのは良い傾向です。夢が広がります。(^^v

 

★APIでtail的なことを行う

これ非常に簡単です。

まず、検索APIをパラメータなしで実行するとJSONで以下のレスポンスが返ってきます。

  • 含まれるログのIDの最小値
  • 含まれるログのIDの最大値
  • 直近のログ100件(生成日時、プログラム、メッセージなどが構造化されています。)

これに対して指定できるパラメータには以下のものがあります。

  • min_id - 指定された場合このID以降のログを取得する
  • max_id - 指定された場合このIDまでのログを取得する
  • min_time - 指定された場合この日時以降のログを取得する
  • max_time - 指定された場合この日時までのログを取得する
  • q - 任意のクエリ(WebUIと同じ書式)

※min_time, max_timeはUNIX TIMEで指定

 

min/maxの指定でどこから、どこまでを取得するかが指定できるわけですね。ちなみに取得件数は常に最大100件で指定できないようです。

なので、最初にmin_timeを指定していつからのログを取得するかを決めたら、あとはそのレスポンスのmax_idを、次のリクエストのmin_idに指定してループをまわせば連続したログが取得できます。

ここでポイントとなることが2点あります。

 

min_idに指定したIDのログは結果に含まれない

min_idを指定すると指定したIDのログを先頭にしてそれ以降のログが取得されそうな気がしますが、実際には含まれません。

なので、max_idを次のリクエストのmin_idに設定する際には+1するとかの小細工は必要ありません。(PapertrailのログIDは連番ではないので+1しても抜けることはおそらくありませんが。)

 

結果が0件の場合でもレスポンスのmax_idは設定される

直近のログまで読んでしまったら、次のログがまだ存在しないということがありえますが、その場合でもレスポンスには最終行のログIDがmax_idに設定されます。

なので「結果が0件の場合に備えて前回のmax_idを取っておいて。。。」みたいなことをする必要はありません。

 

細かいことですが、この2つのおかげでtailライクなログ取得を組む際のロジックがシンプルになります。

 

★ログ解析アプリケーション

そんなこんなで、ログ解析アプリはPapertrailのAPITokenを入力すれば誰でも使えるようになりました。(^^v

 

http://flect-papertrail.herokuapp.com/

 

Herokuアカウントで使う場合は要求される権限の範囲が広すぎるため、試しに使ってみるのも抵抗がある気がします。開発環境で試してみたいけど本番環境まで一緒に見えちゃうよ!みたいな。

が、PapertrailのAPITokenであれば開発環境で試してみるということもできるので、気が向いた人は使ってみてくださいな。

 

□□□□

どうでもいいけどこのアプリ、最初に作ったのが今年の4月頃。そこからずっと放置してたのがこの数日でグラフ機能を追加。この先もまたしばらく放置の予定です。

これをトラブルドリブン開発(TDD)とでも名づけましょうか。(^^;

それでは良いお年を(^^)/

2013年12月24日 (火)

HerokuとWebSocket

WebSocketはいけてる!(^^v

というのが、初めて実際にWebSocketアプリを実装してみての感想です。

前回の予告通り今回はHerokuでWebSocketの話です。
ちなみにアプリ自体は前回のモノと同じです。

http://flect-papertrail.herokuapp.com/

OAuthでHerokuにログインして「heroku logs -t」相当のAPIでログを取得しながら、log-runtime-metricsの数字を拾ってリアルタイムにグラフ表示します。
拾う数字は<KEY>=<数字>という形式であればなんでも良いのでPostgreSQLの出力情報や自アプリで出したログもグラフ化することが可能です。

WebSocketのサンプル題材としてはそこそこ面白いと思うんですが。。。


残念なことにHerokuのログは直近1500行しか取れないので、それなりにアクセスのあるアプリだとグラフにプロットすべきポイントが数個しか取れないという落ちがあります。(^^;

お暇な人はしばらく放置してグラフを眺めてみてください。別に見なくてもここから先の本題を読むのにまったく支障はありませんが。(^^;

 


★ WebSocketアプリケーションの作成

といっても、ここでは作り方の解説はしません。。。(^^;;;
普通にNode.jsとかPlayとかでWebSocketアプリケーションを作成すれば、それがそのままHeroku上でも動きます。

唯一Herokuアプリ特有の処理が必要なのは、HerokuのWebSocketには30秒でのタイムアウトがあるので、それを回避するために定期的に通信を行わなければならないという点です。

これを実現する一番簡単な方法はクライアント側JavaScriptのsetIntervalで定期的にダミーリクエストを送信することだと思います。
 
Play2(Scala)でのWebSocketアプリケーションの作り方は気が向いたら、また別途まとめます。(^^;;;

 


★HerokuのWebSocket

現在HerokuのWebSocket機能はβ版であり、使用するためにはアプリ毎にコマンド

 

heroku labs:enable websockets -a myapp

 

を実行する必要があります。
このコマンドを実行することでWebDynoのフロントにあるLoadBalancerがWebSocket対応版に切り替わります
Dynoの機能として何かが追加されるわけではなく、入口の方が変更されるわけです。

このアーキテクチャのため、コマンドでWebSocketを有効にした後に実際にWebSocketが使用できるようになるまでに若干の時間(DNSの切り替え)を要することがあります。(とドキュメントに書いてあります。)

またAddonとしてSSL Endpointを使用している場合はWebSocketを有効にできません。これはSSL Endpointはほとんどの場合独自ドメインと併用されており、ユーザーの管理するDNSでHerokuのSSL Endpointを指すように設定されているので、そのエンドポイントを勝手に切り替えるわけにはいかないためと思われます。

この場合、一度SSLEndpointを解除して、WebSocketを有効にしてから再度SSL Endpointを追加します。もちろんDNSの再設定も必要なはずです。(試してないですけど多分。。。)


★マルチDynoとWebSocket

WebDynoを複数起動した状態でWebSocket接続を確立するとそこでの送受信は常に同じDynoに対して行われます
逆に言うと他のDynoからはその接続は見えません。

なのでWebSocketのサンプルとしてよくあるチャットルームみたいなものを作ろうとした場合、各ユーザーが同じDynoに接続されるとは限らないので困ってしまいます。。。(--

もっとも、これはHerokuに限った話ではなくスケールアウトするWebSocketアプリには常につきまとう課題です。ざっと検索したところ、この場合Redisのpub/sub機能を使用するのがセオリーのようです。


★Dyno再起動とWebSocket

普通に接続が切断されるだけでしょうね。。。
少なくともコマンドで「heroku restart」を叩いたところでは問答無用で切断されました。

これはよくよく考えると結構悩ましい問題です。Dynoにはデイリー再起動があるので、だいたい1日に1回再起動があるわけですが、このタイミングに存在した接続を救済する方法は多分ありません

対処方法としてはクライアント側でサーバからの切断を検知したら再接続するとか?
けっこうめんどくさそう。。。(--

だけど、この辺どうなんでしょうね?これもHerokuに限らず他の環境でもWebSocketサーバからでも不当に切断されることはありそうな気がするので、まっとうなWebSocketアプリケーションはサーバーサイドからの切断に備えて必ずリトライを実装しておくべきなのかもしれません。

経験者のお話を伺いたいですな。


★モバイルとWebSocket

つねづねかねがね思ってたんですが、劣悪な通信環境や端末がスリープした場合にはWebSocket接続はどうなるの?という疑問があったので何パターンか試してみました。
テストに使用した端末はELUGA P-02E(Docomo, Android 4.1.2, 3G/LTE)とiPad2(iOS7, WiFi)です。
Androidは標準ブラウザがWebSocketに対応していないのでChromeを使用しています。(Can I UseによるとAndroid標準ブラウザも4.4からはWebSocketに対応しているらしいです)


- Android スリープ
スリープしても通信は継続されます。
数分放置しても復帰時に接続は継続されておりおそらくメッセージの欠落もありません。(てことは電池の消費も早くなる気も。。。)

ただ、何回かテストした中で一度だけ接続切れました。

- Android 劣悪通信環境
ほとんどネット接続ができなくなる通信砂漠を経過しても通信は継続されます。
おそらくメッセージの欠落もありません。
どこかでバッファリングされているってことですかね。

どうでもいいですがここで言う通信砂漠は東急日吉駅近辺のことです。

- Android 再起動
切断され、再度ブラウザを開いた時にページ自体がリロードされます。

- iOS スリープ
切断されます。

- iOS WiFi電源断
切断されます。

- iOS 再起動
切断され、再度ブラウザを開いた時にページ自体がリロードされます。

□□□□
だいたいこんな感じです。もっと接続切れるかと思ってましたが、Androidは予想以上に頑張ってました。(^^;

網羅的にテストする気はまったく無いんですが、この結果だけからでもだいたいの傾向は見えてきます。

まぁ、切れる時には切れる(雑)ってことですね。(^^;
そしてそうであれば、やはりクライアント側からのリトライは必要な気がします。

2013年12月20日 (金)

HerokuとJavaのメモリのお話

Heroku上のJavaアプリでNewRelicで見る限りメモリはまだ余裕がありそうなのにR14(Memory quota exceeded)がログに出力されるようになり、アプリの応答が極端に悪くなる(というより1台のDynoがほぼ無応答になる)という事象が発生したので、原因を調査しました。

結論から言うとエラーの原因は特定できなかった(いや、まぁメモリ不足であることはわかっている(^^;)んですが、なかなか興味深いこともわかったのでここにまとめておきます。

 


★ JavaVMのメモリ管理

Javaで使用中のメモリ量を調べる方法としてはRuntimeのメソッドを使用する方法とMemoryMXBeanを使用する方法があります。

http://docs.oracle.com/javase/jp/7/api/java/lang/Runtime.html
http://docs.oracle.com/javase/jp/7/api/java/lang/management/MemoryMXBean.html

前者はHeapメモリのみを対象とし、後者はHeapとNonHeapの両方を対象としています。
なので、Runtimeからとれる値はMemoryMXBeanからも取得できるわけですが、その関係は以下のようになっています。

  • Runtime#maxMemory == HeapMemorUsage#getMax
  • Runtime#totalMemory == HeapMemoryUsage#getCommitted
  • Runtime#freeMemory == HeapMemoryUsage#getCommitted - HeapMemoryUsage#getUsed


ログ等にメモリ使用量を出力する場合、お手軽なのでRuntimeのメソッドの方が使用されることが多いと思いますが、この方法だとNonHeapの容量が含まれていないことに注意する必要があります。

ちなみに今回テストで使用したアプリ(Play1)ではNonHeapは80MB - 100MB位確保されていました。意外と多いです。

 


★ NewRelicのInstancesページに表示されるメモリ使用量


NewRelic自体もMXBeanを使用して情報収集しているはずなので、ここでの各グラフはMXBeanで取得できる値と対応関係があるはずです。

その対応は多分以下です。

  • Physical = HeapMemoryUsage#getCommitted + NonHeapMemoryUsage#getCommitted
  • CommittedHeap = HeapMemoryUsage#getCommitted
  • UsedHeap = HeapMemoryUsage#getUsed

多分というのは、今回アプリの中からMXBeanの値を定期的にログ出力するようにしてその値をグラフ化するということをやったんですが、そのグラフと自前のグラフでいくらか傾向が異なるからです。

ちなみに自前のグラフはこんな感じ。(1分間隔で出力)

 

Memory

 

見ての通り、UsedHeapがかなりギザギザしたグラフとなっていますが、これは定期的にgcがかかっているためと推測されます。

しかし、NewRelicのグラフではこのようなギザギザは観測されません。
複数Dynoの平均値だからかとも思ったんですが、1Dynoの場合でもこのようなグラフとはならないので謎です。。。(--

ただ全体的には同じようなグラフとなるので、ベースとしている値は上記であっていると思います。。。

が、先にも書いた通り複数Dynoを使用している場合はNewRelicのグラフは平均値になるのでなので各Dynoがどれだけメモリを使用しているかはNewRelicではわかりません

 

あと今回の調査とは直接関係ありませんが、NewRelicのagentのjarファイルはちょいちょいアップデートされているので、たまには見直した方が良いかもしれません。

ほとんどの場合、最初にNewRelicを追加した時にDownloadしたAgentをそのまま使用していると思いますが、2系から3系へのバージョンアップではAgentのオーバーヘッドが減っている、とCHANGELOGに書いてあります。(まぁ実感できるものではありませんが。。。この辺Addonが自動的にやってくれると嬉しんだけれど。)

 


★ heroku labs:enable log-runtime-metrics

labsの機能、log-runtime-metricsを有効にすると20秒に一回各Dynoのメモリ使用量がログに記録されます

 

https://devcenter.heroku.com/articles/log-runtime-metrics

 

ここで出力される「memory_total」がHerokuが監視しているメモリ使用量で、この値が512MBを越えるとR14が出力されます。

ちなみにR14が出力される間隔も約20秒おきなので、このスイッチで切り替わるのはログ出力の有無だけでメモリ監視自体は常に実行されているのでしょう。

ちなみに記録対象はすべてのDynoなので、heroku run bashとかSchedulerで動いたDynoのメモリ使用量も記録されます。

各Dynoのメモリ使用量が個別にわかるし、オーバーヘッドもほとんどないと思うのでlabsだからと敬遠せずに全部のアプリで有効にした方が良いと思います。

 


★ Javaのメモリ使用量とDynoのメモリの関係

さて、今回の調査を始めた元々の動機は「NewRelicで見るメモリグラフではそれほどメモリを使用しているようには見えないのにR14警告が出ることがある(気がする)」というものでした。

先のグラフではわざと載せませんでしたが、log-runtime-metricsの値ももちろんログから拾えるのでグラフ化できます。これらを重ねればJavaのメモリ使用量とDynoのメモリの関係がわかるはずです。

その衝撃の計測結果はテストアプリのでのグラフを公開しているので是非直接ご覧ください。(12月16日以降はグラフが見られます。)

 

https://flect-papertrail.herokuapp.com/app/fexp/metrics/2013-12-19?key=memory_rss,memory_total,HeapUsed,HeapCommitted,Physical,PsRss

 

なにが衝撃だったかって、Herokuの計測しているmemory_totalがJavaのHeapCommitted(+ NonHeapCommitted)よりも小さいことがあるということです。

そんなことってある???

僕の認識ではCommittedというのはJavaによって確保済みのメモリなので、それはプロセスが使用しているメモリとほぼ等しいはずと思っていたんですが、外から見たメモリがそれよりも小さいってどういうこと???

そんなはずはねーと思って、アプリ上にpsを叩いた結果を表示するコントローラを付けてみたところ。。。

。。。マジでかーーー。。。ここでもCommittedよりも小さい値が表示されてるよ。。。(--

誰かこの現象を説明してください。m(_ _)m

 

(12/24 追記)

その後ログに「ps aux」の結果から抜いたメモリ使用量を出力するようにしてグラフに追加したところ、ほぼmemory_rssと重なりました。なので外から見た場合のメモリ使用量はJavaのCommittedメモリよりも大きいことも小さいこともあり、その数字が実際のメモリ使用量(Herokuの監視対象)と考えるのが良いようです。

□□□□

話を戻すとグラフを見るとmemory_totalはJavaから見たメモリ使用量よりも小さい時も大きい時もあります。ただグラフの上がり方を見るとアプリの負荷と相関関係があるのは間違いないです。

アプリ以外にメモリを使っているものというのがイマイチ想像できないんですけど、通信バッファとかもあるんだろうか?psにでてこないプロセスも何かある気がする。(根拠なく言ってます。。。)

これはもうこういうものだと思って飲み込んだ方が良いのかもしれません。。。深入りしても満足のいく結果が得られそうな気がしない。。。(敗北宣言)

ログからDyno毎の正確なメモリ使用量を可視化するツールができただけでもまぁ良しとしますかね。。。(--

 

★なぜか次回予告

さて、今回作成したグラフツールはまぁまぁ便利です。(ドキュメントとかまったく書いてないけど)

仕組みとしてはPapertraiがS3にアーカイブしたログを引っ張ってきて解析しているので、前日以前のログは見られるわけだけどPapertrailのログがアーカイブされるのはだいたいお昼過ぎ(昔はもっと早かった気がするけど)なので、「今R14出てるよ!」みたいな時には使えない訳です。

そこはやっぱりリアルタイムで見たいのが人情!

。。。かどうかは知りませんが。(^^;

 

そういやPlatformAPIで直近のログを取得できたな。。。

 

そういやHerokuってWebSocketサポートしたんだよな。。。

 

じゃリアルタイムグラフも作れるんじゃね?(^^v

 

ということで、次回はHerokuとWebSocketの話です。

2013年12月11日 (水)

SalesforceとHeroku Postgresの同期

こんにちは。このエントリはSalesforce Advent Calendarの11日目です。


ノリでSalesforce Advent Calendarに手を挙げてしまいましたが、僕自身はSalesforceでアプリを作ったことは一度もありませんVisualForceは1時間で挫折しました。

そんな人間がSalesforceのAdvent Calendarで何を語ろうと言うのかというと、SOAP APIのJavaラッパーを作ったりもしているので、それを使ったSalesforceと外部RDBMSとの同期連携について書いてみようと思います。

※タイトルは半分釣りでHeroku Postgresと書いていますが他のRDBでも多分動きます。


★ところで Heroku Connect

本題に入る前にSalesforceとHeroku Postgresの同期と言えば先日のDreamForceでHeroku Connectが発表されました。

http://blogjp.sforce.com/2013/11/heroku1-.html

今のところ予告編だけで実際に試せるのは来年以降になるらしいですが、某エバンジェリストに聞いたところでは、これはSalesforce上のオブジェクトと同じ構造のテーブルをPostgres上に作成して双方向に同期するもののようです。

ちなみにガバナ制限は受けない某エバンジェリストは力強く言っていましたが、別の筋からはそんなの聞いてないという話もあり、真偽は定かではありません。(^^;

素晴らしいんですが、この枠組みだとまずSalesforceアプリありきなので、既存のHerokuアプリのデータをSalesforceに取り込みたい場合は使いにくいかもしれません。

今時のクラウドデータベースではストレージ容量は十分過ぎる程にあるので、アプリで使用するテーブルとSalesforceと同期するテーブルはきっぱり分離してPostgres内でトリガーで同期するというアプローチはアリだと思いますが、自前のテーブルの方はともかく自動生成される同期テーブルにトリガーをつけて良いかどうかは謎ですね。。。
それにこのやり方で完全双方向は無限ループの回避がやっかいかな?

いずれにせよ使えるようになったら試してみるつもりなのでこの話はまたいずれ。



★ところで flectSalesforce

ちょいちょいあるよー、とはこのブログでも言っていたのですがちゃんと紹介したことはなかったと思うので改めて。

http://oss.flect.co.jp/libs/ja/flectSalesforce.html

flectSalesforceはSalesforceのSOAP APIのJavaフルスクラッチ実装です。

僕は元々某所でSOAPのかなり初期からSOAPクライアント/サーバーを作ったりもしていたのでSOAPには色々と言いたいことがあるんですが、それはまぁ良いでしょう。

とにかくAxisは使いたくなかったので自分で作ることにしたんですが多分正解でした。SalesforceのSOAP実装にも色々と突っ込み所があるんですが、これまた誰の共感も得られそうにないので墓まで持っていきます。(^^;

□□□□
前置きが長くなりましたが。。。
そんなこんなでこれはSalesforce APIの実装のひとつです。必要なものから作っているので全部のメソッドを実装しているわけではありませんが、オブジェクトの更新/削除など主要なものは網羅しています。(describeLayoutやdescribeTabsみたいなメソッドを実装しても誰も使うことないでしょうしね)

元々は社内で使うことしか想定していなかったのでコメントも日本語です。(^^;
(必要最低限しか書いてませんが)

既存のAPIラッパーとの相違点としては単純なAPIのラッパーに留まらず、独自のユーティリティインターフェースを備えている点があげられると思います。

  • SQLライクなINSERT, UPDATE, DELETE構文によるデータ更新
  • Fixtureによるテストデータの更新、削除
  • RDBからのSELECT結果をSalesforceに取り込み
  • SalesforceからのSELECT結果をRDBに取り込み


などなど。
後ろの二つがここから紹介する内容です。
APIの使い方自体は

http://flect-salesforce-sample.herokuapp.com/

でサンプル付きで説明しているのでここでは主に処理の枠組みについて説明します。


★RDBからのSELECT結果をSalesforceに取り込み

http://flect-salesforce-sample.herokuapp.com/sqlsync

RDBからSELECTした結果でCSVを作り、それをBulk APIで投入します。

特徴的なのはSELECTしたフィールドとSaleforceオブジェクトのフィールドの対応をSELECT文内のエイリアスで指定するという点です。

 

SELECT COL1 as Col1__c,
       COL2 as Col2__c
  FROM TABLE1
 WHERE ...

 

この仕様によりJoinや関数の使用などDBMSのサポートするすべてのSQL構文を駆使してSELECT文を書くことができます。

同期処理として運用する場合はWHERE句に更新日付で結果を絞る条件を付加して定期的に実行します。(それに特化したアプリもあります。)
項目に外部IDがあればレコードの更新方法はUPSERTになります。


ちなみにBulkAPIには1万行制限がありますがSELECT結果が1万行を超える場合は内部的にファイル分割されるので、1万行を越えても問題なくデータを取り込むことができます。


★ SalesforceからのSELECT結果をRDBに取り込み

http://flect-salesforce-sample.herokuapp.com/sobjectsync

これもBulkでやりたかったんですが、クエリ自体はSOAP APIで投げています。
何故ならBulkクエリでは参照フィールド(CreatedBy.Nameとか)が取れないからです。。。


使えねー。。。(--

サブクエリは仕方がないと思うけど、参照フィールドくらいはBulkでも取らせて欲しいよ。。。

□□□□

仕組みとしては要するにSalesforceに対してクエリを実行した結果をぐるぐる回しながら指定のキーでUPDATE文を実行し、結果が0件だったらINSERT文を実行しているだけです。
QueryMoreは内部的に処理されるので、結果セットが大きい場合も問題なく実行できます。

こちらの方ではSalesforceオブジェクトのフィールドとRDBのテーブルカラムを一つずつ指定する形式をとっています。

なのでSOQLの関数は使用できません。(あるんだっけ?(^^;;; )

その代わりSELECTした結果をJavaで加工した結果をマッピングすることができます。

 

request.addFunctionMapping("NAME", new SObjectSyncRequest.Function() {
    @Override
    public Object evaluate(SObject obj) {
        String fn = obj.getString("FirstName__c");
        String ln = obj.getString("LastName__c");
        return ln + fn;
    }
});

 

引数のSObjectから値を取得してそれをごにょごにょと演算をした結果をreturnすればOK。データ移行の自由度はむしろこっちの方が高いです。


実行結果の詳細なハンドリングを行いたい場合はListenerクラスを組み込むことで個別にエラーとなったオブジェクトをハンドルすることができます。

□□□□
こんな感じで雰囲気は伝わったでしょうか?
Heroku Connectが完全自動で固定的なのに対して、こちらの方は自由度高くプログラムに組み込めるのが特徴と言えると思います。

実際のところ、これらの機能は社内要件から実装したものであって公開して誰でも使えるようにしているのは、もののついでという感はあるのですが。。。(^^;

ご意見、ご要望等あればGithubのIssue、Twitter等に書いてくれれば可能な範囲で対応するので、定常的な同期に限らずワンショットのデータ移行の場合などでも思いだしたら使ってみてくだされ。(^^)/

2013年12月 6日 (金)

Heroku Platform APIのJavaラッパー

しばらく別の仕事をしていたため一ヵ月以上放置していたんですが、Heroku Platform APIのJavaラッパーをリリースしました。

 

http://oss.flect.co.jp/libs/ja/heroku-platform-api.html

 

Platform API自体がまだベータでどのように変更されるかわからないのでバージョンは0.9としています。(というかいくつかFeatureRequestしようと思ってます。)

元々は全メソッドを実装するつもりはなかったんですが、7割ぐらい実装した段階で全部作ろうと決心しました。でも、どう考えても使うことのなさそうなメソッドも多いです。(^^;

使い方は多分PlatformAPIのJavaDocとHerokuのDevCenterのリファレンスを並べて眺めればだいたいわかるんではないかと。

- http://oss.flect.co.jp/apidocs/heroku-platform-api/jp/co/flect/heroku/platformapi/PlatformApi.html

- https://devcenter.heroku.com/articles/platform-api-reference

基本的にはRESTのAPIとJSONを忠実にJavaに落としていっただけです。ちなみにJavaDocは全クラス、メソッドがGitHubのソースへのリンク付きという親切仕様です。(^^;

以下、いくつかポイントを絞ってわかりにくそうなところと突っ込みどころを解説していきます。

 

★認証

APIを使用するための認証方法には以下の4つがあります。(括弧内は対応するPlatformApiクラスのメソッド名)

  • OAuth(fromOAuth)
  • refresh_tokenをaccess_tokenと交換する方法(fromRefreshToken)
  • ユーザー名とパスワードをaccess_tokenと交換する方法(fromPassword)
  • ユーザー名とAPIキーを使用する方法(fromApiToken)

一番基本的な使い方はOAuthでの認証です。OAuthを使用するためにはあらかじめHerokuダッシュボードのAccount画面でOAuthClientを登録してClientIDとClientSecretを取得する必要があります。

このJavaラッパーではPlatformApi#getOAuthUrlメソッドでHerokuの認可画面のURLがとれるので、そこにブラウザでアクセスしてリダイレクトで返ってきたcodeをPaltformApi#fromOAuthに渡せば有効なPlatformApiクラスが取得できます。

OAuth認証のレスポンスには常にrefresh_tokenが含まれています。つまり一度認可を受けてしまえばあとはずっとオフラインアクセスが可能です。

refresh_tokenはユーザがダッシュボードでそのアプリをRevokeしないかぎりずっと有効です。

あとはユーザ名とパスワードまたはAPIKeyを使用してAPIを叩くこともできます。(APIKeyはコマンドで「heroku auth:token」を叩くと表示されます。)

パスワードでの認証はaccess_tokenとの交換が必要なのに対しAPIKeyは交換なしでいきなり使用できます

これが可能なのでPlatform APIは実は全部以下のようにcurlで1コマンドで実行可能です。

 

curl -i -H "Accept: application/vnd.heroku+json; version=3"
    -u <USER>:`heroku auth:token`
    https://api.heroku.com/apps

 

★Scope

OAuthで認可を求める際に指定できるScopeは以下です。

  • global(全権限)
  • identity(アカウント情報のみ)
  • read(アプリの環境変数以外を読める)
  • write(アプリの環境変数以外を書ける)
  • read-protected(アプリの環境変数も読める)
  • write-protected(アプリの環境変数も読める)

アプリの環境変数が見えてしまうとDATABASE_URLやその他のAddonの設定情報も丸見えなのでデータベースの中身まで見ることが可能になるわけです。

そのためにread/read-protectedのような分け方がされているんだと思うんですが、全アプリに対して同じスコープが適用されるのでほとんど有効なバリアとはなりません。

現状、アプリ毎に個別に権限が指定できないのがPlatform APIの一番の問題点だと思います。

ちなみにglobalスコープでも実行できないメソッドもいくつかあります。(OAuthClientやAuthorizationなど。これらのメソッドを実行するにはパスワードかAPIKeyでの認証が必要。。。なんですけどそもそも使い道が思いつかないですね)

 

★Formation

とはProcfileで定義するWebプロセスやWorkerプロセスのことです。(前は「Process」と呼ばれてた気がするけどこれから変わるのか???)

heroku psコマンド相当のことはFormationまたはDyno関連のメソッドでだいたいできます。

たぶんこの辺が今のところ一番実用的なメソッドです。

 

★ LogDrain

LogDrainはDynoのログを外部のSyslogに流す仕組みです。PapertrailやTreasureDataなどのログを扱うAddonはこの仕組みを利用しています。

一覧を取得するとそこにはAddonが使用しているURLも含まれているので、それを別のアプリでaddすると複数のアプリから一つのAddonに対してログを流すことが可能です。

やっていいのかどうかはわかりませんが。。。(^^;;;

でも多分Addon側からはログを流してきたDynoがどのアプリに属しているかを知る術はないと思うんですよね。。。

 

★LogSession

作成するとhttpsのURLが生成されて、そこにGETでアクセスすると直近のログが取得できます。URLは5分間または接続してから1時間有効です。(とドキュメントに書いてあります。)

接続してから1時間???

どういう意味かと思ってたら作成時のオプションで「tail=true」と指定すると、レスポンスはContent-Lengthなしで繋ぎっぱなしになります。

なんつー乱暴な。。。(--

しかし実際にtailっぽく動作するので便利は便利です。。。でもアリなのか?これ?

 

□□□□

その他、releasesの一覧は取れてもrollbackはできないとか、全体的に突っ込みどころ満載な感じはありますがFeatureRequestやバグレポも割とちゃんと聞いてくれて実際に修正されたりもしているので、皆もっとガシガシ突っ込んであげると良いと思います。(^^;

2013年12月 4日 (水)

ApexからGoogle OAuthを使う

前回艱難辛苦の果てにApexからService Account認証でGoogle APIを使用することに成功したわけですが、Google APIのラッパーとしてService Account認証にしか対応していないのはどうかと思うので、通常のServer Flowにも対応することにします。

と言ってもVisualForceは昨日Hello worldだけは作れるようになりましたが、コントローラのコードの呼び出し方とか意味がわからないのであきらめました。なので作ったのは例によってApexクラスのAPIだけです。(^^;

 

★ Google Cloud Consoleの設定

Server Flowでの認証を行うためにはGoogle側にRedirectURIの設定が必要です。
アプリケーション自体は前回作成したものを使用すれば良いので、追加で必要な設定は

APIs & auth > Registered apps > アプリケーション名 > OAuth 2.0 Client ID

と展開してWEB ORIGINとREDIRECT URIを設定することだけです。

OAuth認証の受け口はVisualForceの画面になる(多分)ので、以下のような感じで自分の組織のVisualForceページのURLを指定します。

  • WEB ORIGIN: https://c.na14.visual.force.com
  • REDIRECT URI: https://c.na14.visual.force.com/apex/GoogleLogin

 

★GoogleOAuthクラスの使い方

ドキュメントとソースコードは以下です。

 

やってることはドキュメントに書かれていることを素直にコードに落としているだけなので見ればすぐにわかると思います。
必須項目はコンストラクタで引数として渡すようにし、それ以外の項目は任意のプロパティとしています。(access_typeなど2値のプロパティはBoolean型のプロパティとしています。)

それぞれの項目の意味は以下。

client_id:
Google Cloud Consoleで作成したアプリケーションのCLIENT ID

secret:  
Google Cloud Consoleで作成したアプリケーションのCLIENT SECRET

scope:
認可を求める機能のURI。空白区切りで複数設定可。
カレンダーの読み書きを実行したい場合はGoogleCalendarServiceで定数として定義されているSCOPE_READWRITEまたはSCOPE_READONLYを指定すれば良い。

redirect_uri:
Google Cloud Consoleで設定したREDIRECT URI
ここで指定したURLにVisualForceでcodeとaccess_tokenを交換するロジック(GoogleOAuth#authenticateメソッドの実行)を実装する。

state:
任意の文字列。
指定するとredirect_uriで指定したURLに戻ってくる時にクエリストリングとして「&state=xxxx」のように自分が設定した値がそのまま返ってくる。
例えばログイン後に表示したい画面を切り替えるなど開発者側で自由に意味を持たせることができる。

offline_access: (access_type=offline)
offline_accessを使用するかどうか。
offline_accessを有効にした場合、認証のレスポンスにrefresh_tokenが含まれるのでそれを利用してaccess_tokenの有効期限が切れた後でも、いつでもどこからでも何度でもAPIを実行可能になる。

force_approval_prompt: (approval_prompt=force)
Googleの認可確認画面を毎回表示するかどうか。
デフォルトでは表示しない(approval_prompt=auto)となっていて、すでに認可がある場合は確認画面をスキップする

login_hint:
ログインユーザーに関するヒント。
アプリ側からログインユーザーを指定するなどの使い方ができるらしい

□□□□
これらの項目を設定すればGoogleOAuth#getLoginUrlメソッドでGoogleの認証URLが返ってきます。
そのページでアプリを承認するとredirect_uriで指定したURLにクエリストリングでcodeが返ってくるので、それをGoogleOAuth#authenticateメソッドに渡せばaccess_tokenが取得できます。あとは取得したaccess_tokenをGoogleCalendarService#setAccessTokenメソッドに渡せばCalendarAPIを使用できるようになります。(READMEのサンプルがほぼ全てです。)

https://github.com/shunjikonishi/apex-google-api

 

★offline_accessについて

offline_accessを要求して取得したrefresh_tokenはユーザがそのアプリをRevokeするまで自由に何度でも使用することができます。(このライブラリではGoogleOAuth#refreshTokenメソッドにrefresh_tokenを渡すことでrefresh_tokenとaccess_tokenを交換します。)

最初に一度だけrefresh_tokenを取得しておけば、それを延々と使い続けることができるので実質的にはService Account認証の代替として使用することが可能です。

offline_accessにはもう一つService Account認証よりも優位な点があってそれはメールアドレスとして自分の好きなアドレスを使用できるという点です。

Service Account認証ではイベントの作成者欄に表示されたり、招待メールのFromに設定されるメールアドレスはGoogleが自動発行したアドレスになるんですが、このアドレスが「999999999999-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@developer.gserviceaccount.com」(文字数を実際のアドレスと揃えてみました。)みたいな形式だったりするので誰だよ!っとなることは目に見えています。(JWTでprnを指定することで変更できるようですが、権限の設定方法がよくわからず。。。というか機能していない気がします。(--)

この点offline_accessを使用する場合は自分で用意したアドレスを使用できるので、怪しさは軽減されます。(^^;

。。。
どうりでService Account認証に関する資料やブログが少ないわけだよ。。。
誰も使ってないんでしょうね!

採用情報

株式会社フレクトでは、事業拡大のため、
Salesforce/Force.comのアプリケーション
開発
HerokuやAWSなどのクラウドプラッ
トフォーム上でのWebアプリケーション開発

エンジニア、マネージャーを募集中です。

未経験でも、これからクラウドをやってみた
い方、是非ご応募下さい。

フレクト採用ページへ

会社紹介

株式会社フレクトは、
認定コンサルタント
認定上級デベロッパー
認定デベロッパー
が在籍している、
セールスフォースパートナーです。
heroku partnersにも登録されています。
herokuパートナー
株式会社フレクトのSalesforce/Force.com
導入支援サービス
弊社の認定プロフェッショナルが支援致します。
・Visualforce/Apexによるアプリ開発
・Salesforceと連携するWebアプリ開発
も承っております。
セールスフォースご検討の際は、
お気軽にお問合せください。
Powered by Six Apart