2014年7月31日木曜日

【GAE】初級実装編 BigTable導入

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

只今、クラウド基盤「Google App Engine(以下、GAE)」の連載しています。

前回でシステムへのログインに成功しましたので、今度は主立った機能を作って行きます。

しかし、ここで問題となるのが、BigTable。
GAE実装の上での最大の要所です。

機能概要

さて、とりあえず今から時間をかけて作っていく機能は、以下4機能です。

  • 商品の登録
  • 商品の更新
  • 商品の削除
  • 商品の検索

手始めに、代表的なCRUD機能を作って使い方をマスターしていくという作戦なわけです。

RDBMSではない

GAEで使用するBigTableの特徴、それは「RDBMSではない」と言うことです。

「RDBMS」という用語がピンと来ない方もいらっしゃるかもしれませんので軽くご説明しますと、
要はRDBMSとは普通のDBのことです。

「SELECT * FROM aaa,bbb WHERE aaa.id = bbb.id and ……」みたいにSQLを発行して結果を取得出来るアレです。

Oracle、PostgreSql、MySQL、これら代表的は全部RDBMSです。
実際の所、この業界のDBとは殆どRDBMSですので、「DBとはRDBMSのことだ!!」くらいの勢いで考えていても一生困らない人が大半かと思います。

しかし、GAEで採用しているDB「BigTable」はRDBMSの範疇に入りません。

Bigtableは「列指向データベースマネジメントシステム」というカテゴリに入るDBです。

しかし、RDBMSではありませんので、RDBMSの常識がこちらでは通用せず、カルチャーショックを受けることになります。
細かい話は順次判明次第調査していくとしまして、今回はかじり程度にご紹介したいと思います。

BigTableの長所

BigTableの長所は、「大量データOK」と「超高速」の2つです。
何せ、世界中で使われているGoogleの分散システムを支える技術の中枢ですからね、名実共に世界最強の超高速DBだと思います。

具体的には「レコード件数が増えても検索速度は変わらない」という特徴があります。

普通のRDBMSの場合、100レコード中の10レコードを検索するのと、1億レコード中の10レコードを検索するのではパフォーマンスに差が出て来ます。
もちろん大量レコードの中から検索する方が遅い。
なのでDBプログラマーはインデックスを張ったり、テーブル自体を分割したりと、何とかその検索を高速で行おうと日夜チューニングを必死で行っているわけです。

しかし、GAEはそういう心配がありません。
総レコード数が100だろうが100億だろうが、検索速度は常に一定です。
そして速い。

「大量データ、大量アクセスに無敵の強さを発揮する」と言うのがGAEの強みで、そのDBであるBigTableも同様の性質を有しているのです。

BigTableの短所

一方で短所もあるって言うか、RDBMSに慣れている身としては、「大量データと超高速以外は短所しか無い」くらいの気分ですけどね。

一例を挙げると「joinが無い」があります。
同時に2つ以上のテーブルを検索出来ません。

イメージとしては、エクセル表なのですよ。


まあ、縦列と横列の2次元であるという点ではRDBMSもエクセルも同じですが、RDBMSはSQLがあります。
BigTableの場合はフィルターです。


エクセルのフィルター機能だけで検索するようなイメージ。
それがBigTableです。

なので、RDBMSと違って、


  • 複数のテーブルを結合したり出来ません。
  • 「group byでグルーピングして最大値を取得する」とかも出来ません。
  • 「count(*)」も出来ません。
  • 曖昧検索も制限あり。


「え~ッ1? こんな事も出来ないの?」と言うくらいのカルチャーショックを受けます。

もし上記のような機能を実現したい場合は、まずテーブルのデータを全部ガボッと取得してJavaのロジックで算出するとか、そういう泥臭いやり方になります。
もちろん大量データの場合はガボッと全部取得とか出来ませんから、別途集計テーブルを用意しておいて都度更新していくなど、やり方を一考する必要が出て来ます。



「こんなんじゃ開発やってらんないよ!!」



みたいな気分になる事も多いですが、いやいや、代わりに「安い」ですから。
開発が大変になる代わりに圧倒的低コスト運用を実現出来るという可能性を秘めたインフラなので、何とか開発者の方は頑張ったって下さい。

世界に誇るGoogleのGoogle検索、Google画像検索、Googleマップ、Google+、youtube、このブログ。

あれらは全部BigTableで作っているのです。
我々があれらを全部タダみたいなコストで使わせて貰っているのは、開発者の方が知恵を使って頑張って開発してくれたからなのです。

開発者にはかなりの力量を要求されますが、代わりに成功すれば大きなメリットを会社にもたらしてくれるでしょう。

終わりに

ひとまず、BigTableのご挨拶という程度ではこの辺で終了します。

詳しく書いていくと書籍になってしまいますので、このブログでは開発過程で出て来たポイントを都度ご紹介、という形になるでしょう。

次回からは、一先ず簡単な「レコード1件のcreate」から初めていきたいと思います。

2014年7月21日月曜日

【GAE】初級実装編 シングルサインオン

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

只今、クラウド基盤「Google App Engine(以下、GAE)」の連載しています。

今回はシングルサインオンでログイン機能を作っていきます。

画面レイアウト

ログイン画面は以下のレイアウトにしました。


お馴染み「ユーザID」と「パスワード」を入力する欄がありませんね?

はい。
このシステムでは「シングルサインオン」を使ってログイン機能を実装したいと思います。

シングルサインオン

シングルサインオンとは、「一回のログイン認証によって、複数のシステムへ同時にログイン出来る機能」のことです。

シングルサインオンを使用する場合、ログインIDとパスワードは利用先に丸投げすることになるので、
自社でログインIDとパスワードを保持することはありません。

昨今問題になっている「個人情報流出」のリスクも、自社で持たないことで軽減することが可能なのです。


「何も持たないことが長所」


クラウド時代におけるスマートな姿勢の一つですね。

身の回りでよく使用されている例と言えば……、ちょっと「一般的」と言える程普及はしていないかも。

探せばあるんですけどね。
僕がシングルサインオン界で一番幅を利かせていると思うのは、FaceBookでしょうか。
「このシステムはFaceBookのIDでログイン出来ます」というFaceBookのシングルサインオンが最も良く登場するイメージです。

しかし、「常識」と呼べる程は完全普及していないと思います。


私はこの状況は改善されるべきものであると考えています。

処理効率を考えれば、「全人類が共通で一つのアカウントを持っていて、どのシステムでもそれを使ってログイン出来る」という状況がベストです。
世界的な標準化機構が主導して世界統一アカウントを作るくらいの動きがあって然るべきと考えています。

まあ、色々と難しいハードルがあるみたいで、現状、世界統一アカウントと呼べるものは存在しません。

しかし、世界統一アカウントに準ずる存在と言えるものはあると思います。
それが「Googleアカウント」と「AppleID」です。

AndroidスマホとiPhoneを持っている人はみんなこれらのアカウントを持っているわけですから、
現実のシェアを考えると、この2つが世界統一アカウントに最も近いものだと思います。

現在、私が作っているアプリはGAEのアプリですので、ここは「Googleアカウント」に便乗させて頂こうと思います。

「俺のシステムを使いたいならGoogleアカウントを持って来い!!」というスタイルです。

この辺りはアプリの性質を鑑みての判断になるでしょうね。

「いや、このアプリはGoogleIDを持っていない人も使うものだし……」みたいな懸念があるなら普通にログインIDとパスワードをDBに保有すれば良いです。
(代わりに流出リスクという業を背負うことになりますが)

ただ、GAEはGoogleアカウントを使うことで解禁される機能が存在しますので、
GAEシステムの利用はGoogleアカウント保有者に限定し、持っていない人には新規作成するようガイダンスする、という方向で努力した方が合理的だと思います。

開発

では、自分のGAEアプリの中でGAEにログインする方法を解説します。

まずは現状ログインチェックです。

protected void loginCheck() throws ValidationCheckException {

 UserService userService = UserServiceFactory.getUserService();
 User user = userService.getCurrentUser();

 if (user == null) {
  logger.fine("このユーザはログイン認証を行っていません。");
  throw new ValidationCheckException(ResponseCode.NOT_LOGIN);
 }

};

「UserService」クラスの「getCurrentUser()」の結果がnullだったら未ログイン。
nullでなかったらログイン済みです。

そして、nullだった場合は、Googleのログイン画面に飛ばします。

そこから先はGoogleユーザならお馴染みのログイン画面です。
流石に顔を恥ずかしいのでマスクした画像を載せますが、別に正体を隠さねばならない理由はありません。(;^_^)



ここがポイントです。

自分でログイン画面を作るのではなく、Google画面にワープさせて、ログインさせてから戻ってきて貰う。

これによって、自社でパスワード認証をすることは無いので、自社の責任で流出することは絶対に無いのです。

次に問題となるのは、どうやってGoogle画面にワープさせるか、です。その方法は以下になります。

protected LoginOutUrlJet doResponse() throws Exception {

  LoginOutUrlJet jet = new LoginOutUrlJet();

  UserService userService = UserServiceFactory.getUserService();

  jet.setUrl(userService.createLoginURL("戻ってきて欲しいパス"));

  jet.setResponseCode(ResponseCode.SUCCESS);

  return jet;
 }

「UserService」クラスの「createLoginURL()」メソッドで、Google画面のURLを取得出来ますので、
後はこのURLの先にJavaScript等で強制遷移させれば良いというワケです。

ちなみに、このURLは「ログインさせてから戻ってきて貰う」の機能も内包しているものです。

「userService.createLoginURL("戻ってきて欲しいパス")」

こうすることで、ログイン後に自動的に指定したパスのURLに戻ってきてくれるよう、Google側で制御してくれます。

例えば、ログイン後は常にトップ画面に戻ってきて欲しい場合は、

「jet.setUrl(userService.createLoginURL("/"));」と書けばOKです。

分かり易いですね。

終わりに

今回のシングルサインオンは、開発者に「持たないメリット」を理解する手助けになると思います。

昨今は個人情報保護法など色々とセキュリティ面で厳しい世の中になってきました。
その厳しいセキュリティ水準に耐えられるシステムを作るのは……、ハッキリ言って至難です。

「一応ちゃんとやってるつもりだけど、スーパーハッカーに襲われたら知らんし……」
「っていうか、このシステム、品質ボロボロでセキュリティなんてザルみたいなもんだし……」

こんな有様のシステムも少なく無い現状です。
こういう面倒さ、難しさををGoogleに丸投げ出来るのもGAEのメリットなのです。

引き続き、GAE開発を続けて行きます。

2014年7月10日木曜日

【GAE】初級実装編 疎通確認 後半 ~レスポンス送信2~

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

只今、クラウド基盤「Google App Engine(以下、GAE)」の連載しています。

前回でレスポンスパラメータを格納するDTO、「JETクラス」を作成しましたので、
次はそれをJSON変換します。

JSON

JSONは正式名称を「JavaScript Object Notation」と言う、軽量なデータ記述言語です。
JavaScriptという名称が入っていますが、別にJavaScript専用というわけではありません。(変なネーミングですね)

同じデータ記述言語の兄弟分にはXMLがありますが、XMLの方が詳細な情報が定義出来る一方、処理が重くなります。
JSONは余り複雑なデータ表現には不向きですが、軽量という利点があります。

Ajax処理で使用するデータ程度ならJSONで十分対処出来るかと思いますので、基本はJSONを使っていくことになるでしょう。

JSON変換ライブラリ

さて、JavaのBEANクラスをJSON文字列に変換するのは、手動で行う必要はありません。
便利なJSONライブラリがありますので、これを使用しましょう。

  • Jackson
  • JSONIC
  • GSON

ザッと有名所ではこの3つがあります。
世界中でベンチマークが行われており、この中で最も高性能なのはJacksonです。

ですが。

今回はGAEですので、Google製ライブラリであるGSONを使うことにしようと思います。

以下からGSONライブラリをダウンロードして下さい。




しかし、ここで注意事項です。
どうも、最新型であるGSON2系を使用すると、GAE本番環境デプロイ時に「java.lang.VerifyError」が発生するようなのですよ。

どうも不安定らしく、発生したり、しなかったりする困った事象です。

「GAE GSON java.lang.VerifyError」で検索しますと同様の事象が数多く報告されていることが確認出来ますが、解決策は見つかっていないようです。
私も以前にこの問題にぶつかりましたが、その時はGSONのバージョンをGSON1系に下げることで解決出来ました。

この為、私はあえて古いバージョンであるGSON1系を導入することをオススメします。

まあ、VerifyErrorが出る問題につきましても、GAEのバージョンが進んだら発生しなくなるかもしれませんし、
今の所は現場の知恵ということで小耳に挟んでおく程度が良いかと思います。

変換実行

GSONライブラリの導入が終わりましたら、次は実際に実装してみましょう。

JavaのBEANクラスをGSONを使ってJSON文字列に変換するサンプルは、こちら!!


Gson gson = new Gson();
String json = gson.toJson(jet);

これだけ。超簡単なのです!!

そう、私がGSONをオススメするもう一つの理由は、実装が超簡単な点です。

Jacksonだと「Factoryクラス」「マッピングクラス」とか何とか色々ゴニョゴニョしてきますが、GSONは超簡単です。

開発が楽になるという点においても、ここはGSONの採用をオススメしたい所です。

この結果、例としては以下のような文字列が出力されることになります。
これがJSON文字列です。

{"shohinList":[{"key":{"kind":"Shohin","id":3},"seqNo":1,"shohinId":"3","shohinName":"Tacy%E3%83%86%E3%82%B9%E3%83%883","stock":3333,"deliveryDate":"2014/07/04","createdAt":"2014/07/03 14:36:11","updatedAt":"2014/07/03 14:36:12"},{"key":{"kind":"Shohin","id":2},"seqNo":2,"shohinId":"2","shohinName":"Tacy%E3%83%86%E3%82%B9%E3%83%882","stock":1111,"deliveryDate":"2014/07/03","createdAt":"2014/07/03 14:36:01","updatedAt":"2014/07/03 14:36:01"},{"key":{"kind":"Shohin","id":1},"seqNo":3,"shohinId":"1","shohinName":"Tacy%E3%83%86%E3%82%B9%E3%83%88","stock":1234,"deliveryDate":"2014/07/04","createdAt":"2014/07/03 14:35:30","updatedAt":"2014/07/03 14:35:30"}],"responseCode":0}

レスポンス返却

最後に、出力した文字列をHttpServletResponseに乗せて返却すれば一連の処理は完了です。

response.getWriter().write(json);

HttpServletResponseで返却するのはGAEならではの話ではなく、Java Webシステム全般で共通の手法です。
Ajaxを使う時は必ず出て来ますので、覚えておくときっと良いことがあるでしょう。


終わりに

今回は楽勝な内容でしたね。

これでGAE開発の最も根幹部分であるAjax通信は完成です。
後はこれを経由して各種機能をガンガン作って行くだけです。

次回はログイン機能を作ってみようと思います。