GAE BlobstoreでHTTPS経由で認証されたクッキーを見てくれない

September 15th, 2011

#先のエントリーとの時系列的な関係は完全無視で

MagoPicWebはGoogle App Engine(以下GAE)上に実装されています。これはドキュメントも英語版に限り非常によく整備されており、システムのデザインもとても綺麗なのであまり迷わずに採用しました。しかし、まだプレビュー版という事もあり、いろいろとハマりどころがあります。幸い、利用している開発者の数も膨大なため、大抵は先駆者が既にハマってくれていて、slashdot等でworkaroundが見つかります。

しかし、それほどイレギュラーな事をしていないにもかかわらずMagoPicの開発でもコマコマといくつかハマりました。レポートは大分前に出したのですが、昨日ようやくacknowledgeされた一件があったのでここでも紹介しておきます。

http://code.google.com/p/googleappengine/issues/detail?id=5282

まさにタイトルのまんまなのですが、GAEでHTTPS経由で認証されたセッションでBlobstoreへアップロードしたとき、BlobstoreUploadHandlerでセッション情報の取得に失敗します。いくつか回避法はありますが、とりあえず別にセッションIDを生成しておいてアップロード時のパラメーターで渡すようにさせるのが無難かと思います。パラメーターの代わりにcookieに突っ込んだり、あるいはサーバー側でフックしたり色々あるとおもうのですが、MagoPicWebではファイルのアップロードにPluploadというオープンソースのモジュールを使わせてもらっていて、これのFlashモジュールがアクティブな時にcookieが環境によってセットされなかったりされたり不審な動作をしてやはりはまったのでcookieはやめました。

蛇足ですが、GAEの残念な点として独自ドメイン経由HTTPSを使用する手段がないことが挙げられます。仕方なくMagoPicWebにPCからアクセスした場合はHTTPでの通信を行っていますが、Androidアプリ版からの通信はアドレスがユーザーの目に触れることはありませんので全部HTTPSです。

MagoPicまでの長い道のり その一

September 15th, 2011

MagoPic(マゴピク)というサービスを開始しました。

シンプルで規模の小さなシステムですが、プログラムはサーバークライアントともに一人でやりましたので、ここでは暇を見つけて技術的な裏話や開発中にハマった他の開発者の方にも役立つかもしれないTips的な事を無責任に書いていけたらとおもいます。

まずはじめに、MagoPicとは何なのか?

「Yet another 写真共有サービスです」と、いうとまずツッコミが入るでしょう。

  • Picasa使えよ!
  • Flickr使えよ!
  • Snapfish使えよ!
  • フォト蔵使えよ!

おっしゃるとおりです。世の中には星の数ほど写真共有サービスがあり、いずれも非常に洗練されています。みんなも使っているはずです。僕も使っています。たまーに一緒に遊んだ友達に写真を送ったりするときには。しかし、そもそも、作成の動機は完全に自分のためで、離れて住む両親が孫の写真を送れとうるさいので日常の写真を簡単に共有できるものが欲しかったんです。そんな私にとって上記のサービスは最適解ではありませんでした。

  • アップロードするのが面倒くさい
  • アップロードした事を伝えるのが面倒くさい
  • 容量制限がいや(うちは2年間で78GBたまりました)
  • 現像するために縮小版ではなく元のデータを要求される
  • 自分のためにもちゃんと整理したい。バックアップもしたい。
  • 親に使い方を教えるのが面倒くさい

要は面倒くさいんです。

ローカルでは、どうせディスクにちゃんと整理してたまっていっているんです(PCへの取り込みもPicasaのインポート機能はExifをちゃんと判別しないケースがあったりして嫌だったのでスクリプトで自動化していますが)。それ以上の手間なしに同じものを両親にも届けたい。ただそれだけのことがしたかったんです。

MagoPicが出来るまではいろいろな手段を試みました。

リモートでネットワークドライブを見えるようにした

  1. WiFiを使えるようにしてあげるよ、という触れ込みで実家にDD-WRTベースのルーターを仕込む
  2. リモートからSSHで常時アクセスできるようにする
  3. autosshなどを使ってこちらのNASが見えるようにcifsをリモートポートフォワードする

よい点

  • 親の側では一度設定をするだけでよいので超簡単(やってあげればよい)

悪い点

  • 遅い。遅すぎる。(回線が)
  • こちらのNASをずっと動かしておかないといけない → すぐディスクがクラッシュする

実家にもNASを置いて定期的に同期するようにした

回線の遅さの問題と、NASの常時稼働によるクラッシュの問題を解決するために、実家にもNASを置いてssh+rsyncで自動的に同期するようにしました。省エネ機能で自動的にsleepに入るNASのためにrsync前にMagickPacketを送って起こしてあげたりする必要がありましたが、それ以外は何ら問題なく快適な環境が出来ました。

いずれもRAID1のNASで、うちと両親と義理の両親の計三個所(バンクーバー、神戸、島根)に配置されているので冗長性もばっちり!まずデータを失うことはないぜ、これで安心、と思っていた矢先、ディスクがクラッシュしました。想定内なので換装してRAIDを再構築して問題なし、なのですが、この計6台のディスクたちが入れ替わり立ち替わり半年毎ぐらいにクラッシュして差し替えの手間とコストがバカにならなくなってきたのです。

そもそも、私はハードディスクとの相性がすこぶる悪く、職場でも家でも研究室でも個人で使っているマシンの中で必ず毎年1,2個はディスクがクラッシュしてしまうという状況が少なくとも10年以上続いています。使い方が悪いのか、ディスクが悪いのか、私の内面的な問題なのか分かりませんが、いくら冗長化でデータが守られているとはいえ、もうハードディスクを買うのが嫌になりました。

やっぱりCloudしかないべ、でもプライバシーが。。。

ローカルのハードディスクを卒業し、クラウドにデータを置くことにしました。色々調べた結果、HostMonsterが帯域容量無制限でキャンペーン中に登録すると月額わずか$3であることが分かりました。しかしよくよく調べると、ディスクスペースは無制限だが、ファイル数には制限があるとのこと。じゃあブロックデバイス化だ。あと、クラウドはサーバー管理者が内容を見ようと思えば技術的には見れてしまう。それはいやだ。暗号化するしかない。
  1. 容量帯域ともに無制限のホスティングサービスHostMonsterに契約
  2. SSHアクセスを有効にしてもらう
  3. ブロックディスクイメージを作る
  4. TrueCryptで暗号化する
  5. クライアントからはsshfs経由でサーバー上のTrueCryptディスクイメージをマウント

やりました。ついに月額わずか$3と言うハードディスクよりもはるかに安価に無制限で信頼性の高い&セキュアなストレージが手に入りました。

しかし、一ヶ月後、HostMonsterの担当者から連絡があり、規約に違反するので削除しないとアカウントを停止するとのこと。確かに、Webページのホスティング以外の用途に使うなとは書いてある。ごめんなさい。むむむ。確かにsshfsはまずかった。じゃあ公式に提供されているWebDav経由ならどうだ?HTTPだからいいだろう、と思ってしばらくやってみたがやはり同様の警告を受けてしまった。無制限ホスティングサービスといってもやりたい放題ではないのだということを学びました。

挫折、そしてWebサービスで妥協する

クラウド上で暗号化することはとりあえず断念。この頃には既に心も折れていたので、もうオープンソースのWebアルバムでもインストールして手っ取り早く片付けてしまおうと思いました。

HostMonsterのコントロールパネルにあるSimpleScriptという簡単にアプリをインストールできる仕組みがあって、そのリストの中で見つけたZenPhotoというアプリの名前に惹かれて導入しました。これは、ファイルシステム上の写真をそのままWebアルバム化してくれる機能があるので、アップロードもrsyncで一発でできます。そんなこんなで以下の形に落ち着きました。

自分(写真をアップロードする時)

  1. デジカメや携帯をPCにつなぐ
  2. カスタムの全自動スクリプトを実行する(下記はスクリプトの動作内容)
    1. 接続されたSDカードや外部記憶デバイスの中の写真や動画をサーチ
    2. 写真はEXIF情報に基づいて年月日で分類されたフォルダーに移動
    3. 動画はファイルのタイムスタンプに基づいて年月日に分類されたフォルダーに移動
    4. 動画をffmpegでflv形式にエンコードしなおすmakeファイルを実行
    5. エンコードが終わったらHostMonster上のZenPhotoのアルバムディレクトリにrsync
    6. rsyncが完了したら両親の携帯にrsyncのログをメールで送りつける(サーバー管理者バリ)

両親(写真を見る時)

  1. ZenPhotoのURLにアクセスする

写真をアップロードする方も見る方も基本的にはワンステップで。とりあえず面倒くさくなく。当初の目的は達成されました。しかし、後味が悪すぎます。そんなの嫌だ。何より格好良くない。そんなこんなでMagoPic構想が始まるのです。

それにしても、たかが孫の写真共有ごときにここまでするなんて、なんと親孝行な息子だろうと我ながら思う。

た〜のし〜い な〜かま〜で

April 21st, 2011

私はソフトウェアエンジニアリングは極めて泥臭さくあるべきだと思っている。抽象的ではあるがこのドロドロした部分とどこまで向き合えるかで大体のエンジニアの腕が決まってくるような気がする。キモはそういうエンジニアが集まる、或いは育つ環境を作り上げ、プロダクトまでつなげることが出来るのかどうかだ。

そういう観点でumitanuki氏の指摘する [2011-04-18 1人の天才が1000人の凡人をひっくり返すようなエンジニアリングはな] – umitanuki日記 はある意味必然なのだと思う。

優秀な人が大量に集まってもっとを大きな数のエンジニアを有機的に動かしているんだと思います

私は経営、マネージメントに関して全くの素人である上、オープンソースコミュニティの特殊性に関してもシッタカであるが、三人寄れば文殊の知恵だということぐらいはわかる。しかし、人を集めるきっかけを作る、またそのモティベーションを維持しつつ方向性を定めることが出来る先導者の存在は無視できないはずなので、結果的にそういう人たちが天才と呼ばれているのかなと思ったりする。

ただ、企業として、その後も成功を持続させられるか、次の成功を生み出せるかどうか、という長期的な目標を追求するとなると次元が変わってきて、それを成し得る土壌として流動性を抑えたいわゆる日本的な企業風土が効果的に働くと思うのでgamella氏の「日本にマッチするのは「1000人の凡人が一人の天才に負けるエンジニアリング」ではなく「凡人1000人で本当に良いプロダクトを作るエンジニアリング」なのだと思う。」という指摘にも同意。ぽぽぽぽ〜ん。

Titanium MobileでAndroidからTweetする方法 ~続き~

February 22nd, 2011

mogya氏からパッチがうまく動かないというご指摘を頂いたので調査したところ、問題がボロボロと出てきた。

1.evalJS()で例外が発生した時に止まる

TitaniumMobile1.5.1リリース直後のコミットで修正されているevalJS()のバグの影響で、webViewを開いた直後に固まってしまう。Titaniumの今後のリリースでは問題ないはずなので、そのまま放置する。

2.認証ウィンドウでAllowを押しても先に進まない

oauth-adapterの実装由来で認証ウィンドウの後ろにパスワードを保存するかどうかのポップアップダイアログが出てきてしまうことが原因。手元でテストした時には既にその前の試行中に選択済みだったため問題に気がつかなかった。window.open()の位置をwebview追加の後に移動させたら解決した。

3.HTTPClient.openで同期通信を指定しても非同期通信を行ってしまうtitaniumのバグに起因するタイミングの問題

このバグさえ直ればもっとコードがすっきりするのに、そもそもoauth-adapterのapiの設計が同期通信を前提にしているので、無理やりになってしまう。titanium側にパッチを当てた方がよいかもしれない。

4.通信エラーパスにゴミコードが残っていた

コピペ&十分にテストしていなかったことがバレてしまった。反省。

とりあえずAndroidエミュレーターでは安定して動くようになったが、他の環境への副作用が分からないので他のテスト環境が揃うまでパッチは保留。Androidタブレットはさっき家に届いたみたい。iPhone用はSnow LeoperdにアップデートしないとTitaniumがサポートしているXCodeがインストールできないみたい。こっちでは英語版しか手に入らなさそうなんだけど日本で買ったMacBookに適用できるのか?

Titanium MobileでAndroidからTweetする方法

February 21st, 2011

初アプリでtwitterに接続したかったので検索したところ、まさに欲していたものが見つかった。

https://github.com/mogya/tm_twitter_api

しかし、どうやらTitaniumの実装の問題からiPhone/iPadでしか動作しない。Androidでも動かしたいので調査開始。するとoauth-adapterのページに既に先人がいた。

There are a fair number of issues with Titanium and Android, but I was able to get it working on 1.5.1. I can post a diff later, but essentially this is what I found:

  1. Titanium.Network doesn’t seem to work in synchronous mode in Android, had to modify the adapter to use callbacks instead of returns.
  2. The number to string conversion for timestamps in client.send() went into float format, so Twitter couldn’t recognize the timestamp. Put in a .toFixed(0) before it was sent.
  3. webView.html does not get populated on Android, so used the work-around posted here: http://developer.appcelerator.com/question/102231/android-sees-tiuiwebviewhtml-as-undefined
  4. Had trouble getting the source that was fetched with the work-around to parse in the XML utility, so I used a regex instead.
  5. Also noticed that the regex on Android+Titanium doesn’t seem to populate captures if there’s more than one capture defined.

コードがまだ出ていなかったので、自分で試したところ1,2,3は氏の指摘通り、3,4,5に関してはevalJSを使えば一行で解決し無事動作が確認できたので、パッチmogya氏に送る。世の中には同じことをしようとしている人が沢山いるもんだ。

Titanium MobileをProxyの後ろでUbuntuにインストールする方法

February 16th, 2011

友人と話していてちょっと軽くアプリを組みたくなったので、環境を揃えた。基本的な機能しか使わない上、労力を最小化したかったので、iPhone/Androidのクロスプラットフォーム開発環境でよさそうなTitanium Mobileを使うことにした。

インストーラーをダウンロードして起動すると、インストール先を聞かれるが、その後のステップでモジュールのダウンロードに失敗して終わる。プロキシサーバーを通してしか外に出られない環境なのでダメなようだ。コンソール出力を見ているとプロキシの設定を探そうとしているようだが、http_proxy等の環境変数を設定してもダメだったので、仕方なくwireshark。どこかに設定ファイルがあるのだろうが、見つからなかったので諦めた。

DNSクエリはapi.appcelerator.netとcloud2.appcelerator.netの二つ。ひょっとしたら二つ目のは負荷分散のために毎回微妙に変わるかもしれない。とりあえず/etc/hostsを編集してこれら二つのDNSをローカルホストを指すようにする。ついでにインストール後に必要になるwww.appcelerator.comも追加。

127.0.0.2 api.appcelerator.net
127.0.0.3 www.appcelerator.com
127.0.0.4 cloud2.appcelerator.net

ローカルにバーチャルなインターフェースを作ってルーティングの設定をする。

/sbin/ifconfig eth0:1 127.0.0.2
/sbin/ifconfig eth0:2 127.0.0.3
/sbin/ifconfig eth0:3 127.0.0.4
/sbin/route add 127.0.0.2 dev eth0:1
/sbin/route add 127.0.0.3 dev eth0:2
/sbin/route add 127.0.0.4 dev eth0:3

corkscrew経由のsshでどこか適当なsshサーバーを介して上記の仮想インターフェース宛のhttp/https宛のパケットをリモートからフォワードするようにコネクションを張る。

ssh -L 127.0.0.2:80:api.appcelerator.net:80 -L 127.0.0.2:443:api.appcelerator.net:443 -L 127.0.0.3:80:www.appcelerator.com:80 -L 127.0.0.3:443:www.appcelerator.com:443 -L 127.0.0.4:80:cloud2.appcelerator.net:80 user@sshhost

後は、起動したときにハマる可能性として、libgのコンフリクトがあるが、これは公式ページのQ&Aの通りにすれば解決する。

ユニバーサルデザイン

February 13th, 2011

iPhone4で遊ぶ1歳半児達。普通にボタン押してスワイプしてロック解除してた。すごすぎる。この子たちの未来は一体どうなっているのだろう。ちなみにiPodTouchはSony製のDockに刺さっているところをへし折られ、ボタンが効かなくなりました。あの端子だけで固定しようとする設計、ほんとやめてほしい。

ノキアに期待したこと

February 11th, 2011

批判ばかりしていても仕方がないので、私が描いたノキアの再起に向けての短期的なシナリオを書いておく。言うは易しである。

Dalvik VMをSymbianに移植する

これだけでよい。これで少なくとも2年は延命できた。NativeClientとHTMLとJavaScriptが取って代わるまでの時間稼ぎが出来たのだ。

ノキアの資産は低価格帯の膨大なユーザーベースであり、ここを如何にして死守するかが生死の分かれ目なのだ。ハイエンド向けのWindowsPhone7を採用するという判断は、ノキアにとってその唯一の資産をドブに捨てることを意味し、結果的にマイクロソフトが停滞しているスタートラインまで引き返し、お互いの足をくくりつけて二人三脚でスタートを切り直すようなものだ。AndroidやWindowsPhone7が選択肢である時点でその先に差別化は見込めない。どちらも正解ではなかったのだ。

ではなぜDalvik VMが問題を解決するのか。

  • 膨大なJ2MEの資産が生き返る(パフォーマンス的にも)
  • 多くのAndroidアプリをそのまま移植できる(Androidデベロッパーを共有できる)
  • Androidのエコシステムからは独立を保てる

この他にも、例えばDalvikとSunJavaの間の子を作ることはOracleにおいしい話として持ちかけることができたりと、連鎖的に色々出てくるがそれは長期的な話になるのでここでは書かない。

新規アプリの開発 > 異なる開発言語からの移植 > 同じ言語、異なる機種からの移植

Javaデベロッパー > iPhoneデベロッパー > Androidデベロッパー > XNAデベロッパー

新しくデベロッパーを取り込むためにMicrosoftの開発環境が効果的に働くという認識は間違いだ。フリーミアムと同じで、何事もゼロからではドロップ率が高いというのが一番の理由。Symbian上のC++プログラミングが煩わしいのは認めるが、煩わしさを取り去ることに四苦八苦してよりフラグメンテーションを増やした上で新たなデベロッパーを募るのか、あるいは既に存在するコンバートできるポテンシャルの高いデベロッパー層に直接訴えかけるのか、さらには既に手持ちのポートフォリオを流用出来るところまでお膳立てするのか、効果の差は歴然だ。

Qtは所詮デスクトップ向けにデザインされた抽象化レイヤに過ぎない。ノキアはSymbianを失うと同時にアイデンティティを喪失する。WindowsPhone7上にDalvik VMを持ってくるなんてことをMicrosoftが許すわけがない。

ちなみに私は過去三年間、社内で高速なJavaVMの必要性を訴え続けてきた。しかし、Java=遅いというそこらで聞いた程度の知識しか持たないプロジェクトマネージャー達にとっては悲しいかな理解してもらえなかった。

下っ端エンジニアの技術に偏った屁理屈に聞こえるかもしれないが、この仮説が間違っているとしたら私が学ばなくてはいけないことは私の想像以上に膨大であるということになる。それならそれでよい。

破壊的インベージョン

February 11th, 2011

予想通りの展開に失望やでほんま。

結局Microsoftによる戦略的なNokiaの獲得計画に則った茶番かい。

http://conversations.nokia.com/2011/02/11/five-reasons-to-get-excited-about-a-microsoft-partnership/

重心が明らかにローエンドであるSymbianをハイエンド限定のWindowsPhone7にハンドオーバーするという点だけとっても中途半端に価格を下げる負担でNokiaは窒息する。NokiaはSymbianを捨て去るユーザー向けのよい口実を得たに過ぎない。

贅肉をすべて削ぎ落として破壊的イノベーションを起こすまでの時間稼ぎだとしたら、タイムフレームが問題になる。両足を突っ込んだらおしまいですぜ。

Nokiaが3年で地に堕ちた理由(3) ~グローバルカンパニー~

February 10th, 2011

たとえ、山ほどの人材を持っていようとも、愛がなければ、無に等しい

前の二つのエントリーにも共通して言えることだが、とにかくNokiaには無駄が多いのだ。そしてその無駄を無駄にして終わっているのだ。このエントリーも一言で言えば無駄に対する指摘なのだが、その対策が異なる。

Nokiaは10万人を越える社員を抱え、そのサイトは世界各地に広がっている。まるで同じ部屋にいるかのようにビデオ会議ができるシステムや、何百人も同時に参加できるVOIPカンファレンスシステムなどが整備され、会議といえばオンラインが当たり前だ。しかし、11日の発表はオンサイトのみで行われるという異例の通達がでており、社員の間でも様々な憶測を呼んでいる。

それは置いておいて、とにかくこのオンライン会議が毎日ある。理由はプロジェクトが世界中に分散しているからだ。正確に言うとより細かいレベルで担当者が世界中に分散しているのだ。例えばプロジェクトマネージャーとアーキテクチャのデザイナーとサーバーサイドのプログラマーとクライアントサイドのプログラマーとテスターがそれぞれ別のタイムゾーンで働いていたりする。ちなみに任天堂も世界中で製品を売るが、開発は99%京都だ。

入社当初は「さすがグローバルでモバイルな会社だ、高度にシステム化された上流工程とエンジニアリング手法が確立されているに違いない」と任天堂の閉塞感から開放された反動もあって大きな期待を寄せたのだが、やはりこれは正常に機能していなかった。しかし、その原因は組織構造や仕組みではなく結局「人」に帰結するというのが私の結論だ。

分散して開発を行う場合、各々が次の認識を共有しなければならない

  • プロジェクト全体に対する責任感とプロ意識
  • 成果の恩恵を受けるターゲットに期待される効果
  • 関連する他のプロジェクトの把握と想定されるリスク

これを実現する方法を一言で言えば「思いやり」に尽きる。

例えば、日本の企業の場合(少なくとも任天堂では)、他部署にメールを送るときにはまるで社外とやり取りでもするかのごとく気を遣う。相手のリソースや前提知識、状況を無視した一方的な依頼はご法度だ。逆に、他部署から個人的に依頼が来たとしても、引き受けることによる自部署への影響を検討せずに独断することは許されない。これを侵すと計画に制御できないノイズが混じることになる。また、部署間に限らずに思いやりに欠けたメールは、冗長なやり取りや誤解、受け手における無用な調査などを引き起こす原因になる。

自分の行動がもたらす影響を、一緒に働く同僚について思いやれるのか、プロジェクトまで思いやれるのか、会社全体か、あるいはユーザーまで意識して仕事が出来るのか。この思いやりこそがプロフェッショナリズムに直結するのだ。

日本の新入社員が始めに身につけるであろう最低限の常識がNokiaの(少なくとも私の関わった)グローバルなプロジェクトには存在しなかった。その素地の上で展開されるグローバルなコミュニケーションはまるでクリティカルチェーンで各地を結んだようなものなのだ。

少なくとも私がこの三年で目にしてきたプロジェクトの多くは発散し、消えて行った。私は新しい仕事が舞い込んでくる度に、そのプロジェクトの正当性に対して意見しなければならなくなった。それでも強行される場合は、いずれそのプロジェクトがキャンセルされるであろうリスクを考慮した上でリソースをアロケートするという、まるでお互いを危険視し会う体制に収束していった。全くお粗末な話である。