[ad][javascript] WEB+DB PRESS vol.52 - JavaScriptでクロスドメイン通信

すいません、宣伝です。もうすでに発売済ですね。まったく時期を逸しました。

WEB+DB PRESS Vol.52

WEB+DB PRESS Vol.52

あらためて読み返すと、今回の記事は結構わけわからない方に行っちゃった気がします。まあ要は Shindig、というより rpc.js 変態だということですかね。

僕のはさておき、他の記事が大変すばらしいので、買うべきだと思います。

Google Friend Connect を埋め込んだサイトをブックマークレットで乗っ取って借用して自作のガジェットを動かす

Google Friend ConnectJavaScript埋め込むだけでソーシャルアプリを動かすことができてとてもらくちんですね。ただ、いざアプリ(ガジェット)を作って自分で試そうとしても、Google Friend Connect を埋め込んだサイトに誰も登録してくれなければ、ソーシャルグラフの情報を使ったアプリはテストすらできません。テストのためだけにダミーアカウントを作るのも寂しいものです。

これをつかうと、すでにソーシャルグラフが完成している他のサイトのGoogle Friend Connect乗っ取り借用し、自分のガジェットを試すことができます。

http://gfcjack.googlecode.com/svn/trunk/bookmarklet.html

利用方法

上記サイトの「opensocial gadget url」に自分が作ったgadgetのXMLファイルを置いたサイトのURLを入れて、createボタンを押すとブックマークレットへのリンクが自動作成されます。

ブックマークレットを登録して、Google Friend Connect (In Page APIでなくてガジェット/サイドバーでも可) を埋め込んだサイトで起動します。ページにオーバーレイしてフレームが現れ、その中でガジェットが表示されます。

試しにPatrickのサイトで勝手にFriendIntroducerを起動してみる

こんな感じで描画表示されます。

ただしGFC APIで埋め込んだガジェットからのrequestNavigateToがうまく行ってないので肝心の紹介文が書けませんでしたが、生成されたbookmarkletのパラメータに「presentation=canvas」としてを追加して直接canvasビューを立ち上げれば行けます。詳細はgoogle codeのソース参照。

動作について

ガジェット内で取得できるOwnerやOwnerFriendsの情報はそのブックマークレットを起動したサイト/サイトへの登録者になります。Viewer/ViewerFriendsはそのまま自分ですが、ViewerFriendsはGFCの場合そのサイトに登録した人の間だけになる仕様だったはずですね。自作/他作、サイトの所有者との信頼関係は問いません。寄生ガジェット。

ガジェット以外にも、OpenSocialAPIを書いたスクリプト(JavaScriptファイル)をどこかに置いておき、Google Friend Connect APIのIn Page APIを使ってOpenSocial APIの呼び出しができます。この場合、「opensocial script url」にスクリプトを置いておいたURLを指定してブックマークレットを生成します。スクリプトの例はこちら

補足

ただ、作っては見ましたが、これは倫理的にちょっと問題がある可能性があります。ソーシャルデータのフェッチだけなら別にGoogleが面倒見てくれるのでサーバに負荷かかるとしてもたかが知れたものでしょうが、内部でSIGNED makeRequestしたりActivityを流したりPersistに永続データを保存するようなスクリプトについては、既存のサイト内で動かしているアプリ/ガジェットに影響が出たりしないとは言えないですね。程々にした方がいいです。

逆に言うと、こうやってユーザ側で勝手にサイトを乗っ取るのはOpenSocialのガジェット側としては想定の範囲内であるべきで、それに動作が左右されるようなものはそもそもよくないということでもあるかもしれません。

Google Apps Script を試す

I/Oでの目玉って、ほんとはWaveなんかよりこれでしょ。Waveが流行るの待ってたら少なくとも1年以上かかるでしょ。ぼくは今すぐそこにあるものが大好きです。

案外はやくうちのApps(Standard Edition)でも有効にしてくれました。これだけのためにPremierでもいい、とおもったくらい。

Hello World的なもの

コード
function hello() {
  Browser.msgBox("Hello, World");
}
結果

入力プロンプト

次は入力プロンプトを出してみる

コード
function helloWithPrompt() {
  var name = Browser.inputBox("Enter your name");
  Browser.msgBox("Hello, "+name);
}
結果


単純なんだけど、これ、サーバサイドJavaScriptですよ。普通にWebアプリを書いていると、プロンプト出したらリクエスト処理は終わっちゃうのですが、これはつまり継続(Continuation)が実現できてるってことですよね。むかしからRhinoでContinuationがあるのは知ってましたが、実際にホスティングされてるのを触ったの初めて(Apps ScriptsがRhinoかどうかはしらないけど)。まるでクライアントアプリ書いているみたい。これはいい。

カスタム関数

単純に引数受け取って返す関数を作ればいい

コード
function shohizei(n) {
  return Math.floor(n*1.05);
}
結果


CSVでアクセス

上で作成したスプレッドシートを公開して、CSVでアクセスしてもちゃんと値がとれるかどうか確認してみる

http://spreadsheets.google.com/pub?key=rwJ9YlGPGQvLfJpk7g_G1kA&output=csv

はい、これで絶対にサーバ側で動いていることがわかった。

無限ループ

ちょっとおこられそうなことをしてみる。想定ではSandboxが監視して途中で止めてくれるはず。

コード
function infLoop() {
  var a = [];
  while(true) a.push('evil!');
}
結果

予想通り。Maximum Instruction Countってどれくらいなんだろう?どっかのFAQに書いてあるかな?



とりあえずこんなもんにしておく。やっぱすごいとおもいます。

WEB+DB PRESS で連載やるみたいです

なんで声かかったのかよくわかりませんですが、JavaScriptの活用法、的な連載をすることになりました。ストックそれほどないので、多分ネタが途中で切れる気がします。走りながら武器拾います。

初回はブックマークレットでスクレープする話です。なんかどっかで聞いたことあるかも


WEB+DB PRESS Vol.51|技術評論社

(↑ GAE/J, BigTableがおもしろそう!)

会社とは関係なくやってますが、人が同じなので、そういう意味ではいろいろかぶってます。

Tokyo Cloud Developers Meetup #02

GAE特集。銀座のリクルート会場。眠いがちょっとだけまとめ。

発表

Fred Sauer

スケーラブルなアプリをGAEで作る話。GDDでやってたのとおなじらしいが、自分は聞くのは初めて。
JOINに関して、Bigtable自体はjoinはないが、AppEngineのDataStoreでmerge-joinを実装できている。zig-zagにkeyを辿って行く感じ。
Oracleの実行計画を見るとよく出るMERGE JOINを思い出したが、多分戦略としてはだいたい同じで、違うのはBigtableがそもそもSortedだということか。

松尾さん

GAE用フレームワーク"Kay"のお話。django的。sessionストレージがクエリじゃなくkeyによるgetでできたり、GAEに最適化してる。ついさっきmemcacheも使うようにしたみたい。同梱のWerkzeugというdebuggerが秀逸。

大悟さん

konomiというGAE上のフィード共有アプリについて。RSS登録をGoogle Spreadsheetにして開発を省エネ。

矢野さん

wicketをGAEで動かす話。wicketは面白そうだな、と思ったが、多分wicket/javaをある程度知ってるじゃないとそもそもどこが問題だったか入り込めないなあ、と感じた。なので、もちょっとwicketがすばらしい理由を(くどくならない程度に)教えていただければうれしかった。個人的には、認証やロールによるアクセス制御がGAEの認証システムから透過的になるようにいろいろやってる、というところが、取り組みとして興味が持てた。

ひがさん

Type Safe! TDD! Hot Deploy! 以上。

あんどうやすしさん

JRuby on Rails on GAEをもっと簡単にするためにいろいろがんばったよ!という話。いろいろ書き換えちゃったんでヴァージョン依存が大きいのはごめんね、みたいな。

小泉さん

PHP on GAE。Quercusというのを使ってる。

感想

言語 on JVM & フレームワークの話が多かった。勉強としては悪くなかったけど、やっぱ肝はscalabilityのためのtechnologyだったりそれに伴うsandbox/quotaなのかな、と思ってたので、個人的にはちょっと的が外れた感じ。

懇親会はいろんな話ができて面白かったとおもいます。OpenSocialやWaveのbackendとしてもGAEは使いやすい。とくに、OpenSocialのPersisitのいけてなさを補うためにガジェットデータの永続をホストしてくれるサービスがあるらしく、おそらくそれもGAEで動いてんじゃないかとのこと。だとするとPaaS on PaaSですね!

個人的に、発表内容から直接というわけではないのだけど、あることに対してヒントが与えられた気がした。いつかちゃんと調べてみたい。

Salesforceのデータベース

すごい良い記事。

知られざる「マルチテナントアーキテクチャ」(2)~スケーラビリティのカギは組織ID - Publickey
知られざる「マルチテナントアーキテクチャ」(3)~スキーマとメタデータの謎 - Publickey

頼まなくてもこういう記事を書いてくれる人が増えている(頼まれてないっすよね?)のは、SFDCにとって、デベロッパマーケティングとして成功だと思う。大事にしてくださいね。

スキーマについて

上記記事では、データベースの物理スキーマについて、公開情報をもとに分析されています。

掟破りのスキーマ

セールスフォースのアプリケーションで扱われるすべての数字や文字のデータは、Dataテーブルに保存される。このことがセールスフォースのデータベーススキーマの核心です。

Dataテーブルはどんなデータでも受け入れられるよう、次のようなスキーマになっています。恐らく、データベースに詳しい方がみたらびっくりするようなスキーマです。

知られざる「マルチテナントアーキテクチャ」(3)~スキーマとメタデータの謎 - Publickey

ただ、この使い方ですが、それほど実は変な使い方ではないような気がします。少なくとも、使い方の一つとしてこういう「何でもテーブル」は現場でよくあった、と聞いてます(もちろん性能などのトレードオフを考慮した上での適用ではあったでしょうが)。その場合のインデックスについても然りです。
SFDCがスキーマ情報を公開しているのは、そういう意味でそこはもはや共有ノウハウに近く、別に差別化する所ではない、ということでもあるかと思います。

さて、これが本当にRDBMSであるべきか、という点については、ちょっと微妙に思います。まあリレーショナルでなくても、DBMSとしての扱いやすさ、という面でOracleは悪くないのかもしれません。

組織IDによるパーティショニングについて

続いてパーティショニングについて。

すべてのデータに振られる組織ID

セールスフォースはすべてのユーザーが1つのデータベースを共有するマルチテナントアーキテクチャを採用しています。ということは、ユーザーが扱うデータはすべて1つのデータベースに格納されるのです。

しかし当然ながら、ある企業が他の企業のデータを参照してしまうことがないように、厳重なセキュリティを施さなければなりません。そこで、セールスフォースのデータベースに格納されるデータには、必ず「組織ID」が付けられます。スキーマとしてテーブルに組織IDの列が設けられているのです。この組織IDを超えてデータが参照されないようにシステムは設計されています。

そしてこの組織IDが、分散処理によるスケーラビリティのカギにもなっています。

知られざる「マルチテナントアーキテクチャ」(2)~スケーラビリティのカギは組織ID - Publickey

Salesforceのスケーラビリティは、組織(Salesforceを利用する企業)単位で局所化していることにつきるような気がします。この辺が、企業向けサービスであることの優位性というか、そのままコンシューマサービスのスケーリングのテクノロジーになかなか当てはめられない点でしょうか。

そして、これの最大の弱点は、組織を超えたシェアリングはできない、ということです。一応Salesforce to Salesforceという組織間での共有の仕組みはありますが、これはリアルタイムでない。キューを使った同期になります。なぜならパーティショニングどころか、異なる組織ではインスタンスが違ってる可能性すらあるので(PODアーキテクチャ)。

ただ、たとえばCRM業務に限定すれば、企業間でのデータ共有というのはそれほどなく(パートナー企業同士の場合などあったらいい場合もあるが、おそらく必須ではない)、べつにスケーラビリティを犠牲にしてまでリアルタイムでやることではない。むしろ、キューを使ってでも同期の仕組みが標準でできるところにmulti-tenancyの強みがあるとも言えます。

SaaS

こうしてまとめてみると、セールスフォースはSaasに最適化したシステムを前提に全く新規にシステムを設計・構築したからこそ、こうした大胆なアーキテクチャを採用できたのだ、ということがよく分かります。

既存のアプリケーションをもしもここで紹介したようなマルチテナントのアーキテクチャに書き換えようとすると、SQLなどデータアクセス部分は全部書き換え、オンプレミスでは必要なかった組織IDごとのセキュリティモデルは作り込みが必要になるなど、新規にアプリケーションを開発するのとそれほど変わらないか、それ以上に苦労することは間違いないでしょう。オラクルやSAPといったオンプレミスのアプリケーションを基にSaaSを展開しようとしているベンダが、マルチテナントアーキテクチャを採用できないでいる理由もこの辺にあるのではないでしょうか。

知られざる「マルチテナントアーキテクチャ」(3)~スキーマとメタデータの謎 - Publickey

僕もそう思います。彼らがよく言う「既存のアプリがそのまま動く」ということは、つまりスケールのところについて何も考えないままである、ということであり、つまりコストはすべてユーザに転嫁されたままである、ということでもあります。マシンの仮想化レベルで何とかなる話のスケーラビリティはたいしたことじゃないと感じます*1

*1:いつかNRIの城田さんが「真のSaaSは無料トライアルがすぐにできるかどうか」だと判断基準を語っていらっしゃいましたが、マシンレベルの仮想化での提供になると現状やはりそこが難しくなる

Google Friend Connect API に gadgets.io.makeRequest 相当を自前で実装する

Google Friend ConnectOpenSocial APIには gadgets.io.makeRequest が含まれていない(今のところ)。もしあると個人的にちょっとだけ嬉しかったりするので、作った。これでHTML側ではGFCのスクリプトをロードするだけで、任意のサーバに対してHTTPリクエストを送ることができる。リソース側にJSONPインターフェースとかcrossdomain.xmlは必要ない、いわばGFCをオープンなクロスドメインプロキシとして使っている。

e.g.
http://gfcxd.googlecode.com/svn/trunk/xd-test.html

opensocial-jquery版。ちゃんと動く。

http://gfcxd.googlecode.com/svn/trunk/opensocial-jquery-test.html

ContentTypeがDOMの場合はRPCでJSONシリアライズができなさそうなので、たぶん動かない。単純にmakeRequestのコールバック結果をそのままRPCに渡してるのがいけないだけで、やろうとおもったっらページ側でXML parseしなおしてやればいいはずなので、これは単なるさぼり。

さっき見たけど、IEで動いてなかった。なんでかな。あとで調べる。

中身

作るにあたって、Google Friend Connect APIアーキテクチャを若干解析してみた。GFCのOpenSocial API呼び出し時にサーバへの通信がおこなわれるが、これはすべてガジェット経由のフレーム間RPCになっている。OpenSocial API通信のためのガジェットみたいなのが用意されていて(http://www.google.com/friendconnect/gadgets/osapi-0.8.xml)、これらがコンテナ側(GFCスクリプトをロードしたページ)と協調動作する。

ここで、実装の方針として、同じようにgadgets.io.makeRequestをプロキシしてやるようなガジェットを作ってやって、それ経由で呼び出してやればOKだろう、と考えた。

そこで、とりあえずやってみたのだが、ガジェット=>ページはできるけど、ページ=>ガジェットへの通信ができない。いろいろ調べて、Shinding実装のRPCの原理から考えて、双方向通信のためには、ページ側のRelay HTMLだけじゃなく、ガジェット側のRPC Relay HTMLがどこにあるか知ってないといけない、ということに気づく。

ただし、そもそもガジェットのURLはそれぞれ個別のドメインがわりあたえられるので、その場所をどうにかしてページ側で把握しておく必要がありそう。GFCのOpenSocial API呼び出しのガジェットの場合はどうやってるのかな、と思ってみてみたら、ドメインまで含めてスクリプト中にハードコードしてるようでした。なので、FriencConnectのコンテナはガジェットXMLに対して常に一定にドメインを割り振るのだろうと解釈し、こっちのガジェットも調べたURLをそのままハードコードしてる。

google.friendconnect.container.setDomainは、ガジェットのRPC Relay のベースURLを伝えるためのAPI。ドキュメントにはないので、おおっぴらに使うのは避けたいところ。

その他

YQLがある時代になにやってんの?という突っ込みはあまり期待していません。POSTができたりとか、将来的にOAuth Proxyできるかも、みたいなところがちょっといいかもね、とおもいます。