ikeike443のブログ

ソフトウェアビジネスに関心がある系のブログ

Play! framework 概要 Tipsもあるよ!

Play! Advent Calendar 2011 一日目ということで、軽めの話をします。

おさらい:Play!とは

Play!はJavaで軽量に素早く開発できるフレームワークです。


Play!についてよく知らない人が圧倒的多数だと思いますので、ものすごく簡単に説明しますね。


Play!はJavaEEの仕様を捨ててWebとフォーリンラブすることに決めたフレームワークなので、Servlet特有の変なセッション仕様なんてないですし、えっと、セッションレプリケーションってなんですか? ってなノリのフレームワークです。
もちろん、warにパッケージングする必要もないです。


EclipseのECJを使って動的コンパイルを行うことで、ほんとうの意味でのホットデプロイを実現しています。というか、デプロイしないんですけど。。まあ、ほとんどスクリプト言語のようにJavaを使えるわけです。再起動無しで変更が反映されるって素敵。


モデル周りは、JPA、っていうかこれJPAなのかなー。。って思うくらい手を入れてますが、そのおかげでPlayではDBスキーマの変更もJavaのコードを書き換えるだけで自動でやれちゃいます。DBスキーマの変更も再起動無しでやっちゃうわけですね。
もちろん、この機能はプロダクション環境で動作すると危険ですので、環境によって動作を変えるなんてのも容易です。


今時のプラグインが揃ってる

プラグインシステムも用意されています。
ここを見ると分かるんですが、Javaとは思えないイケてるラインナップっぷり。
MongoDB、Redis、CoffeeScriptやSASSなんて当たり前のようにサポートされています。

Play!を好んで使う開発者っていうのが、どんな人達なのか、よくわかると思います。
HerokuがネイティブにPlayをサポートしたのも当然だなあなんて思いますね。

ノンブロッキングもあるよ!

また、最近Node.jsで注目度を高めたノンブロッキングI/Oもサポートしています。興味のある人はこのへんのコード*1や、このへんのコード*2を読んでみるといいと思います。また、多分誰かがこの辺詳しく突っ込んでくれるでしょう!


これでノンブロッキングI/Oといえば、JavascriptならNode.js、PythonならTwisted、RubyならEventMachine、JavaならPlay!と言えるわけです。よかったですね!


Node.jsとPlay!を比較した記事なんかもありました。レンダリングを除いたI/O部分だけ見れば、Play!はNode.jsに比肩する部分もある(2011年3月時点の話)んですよね。レンダリングを含むとNode.jsには全然勝てないんですが。。
詳しく知りたい方は、翻訳しておいたので下記を見てください。まあこの記事は半年も前なのであまり参考にはならんかもですが。。
https://www.evernote.com/shard/s18/sh/d356225c-93ab-4fb2-9a7c-178eb23045e9/d82052027a5bb481579eaa4691d53dfb



そしてScalaへ、そしてPlay2.0へ

っていうここまでが、Playの1.x系のお話。
このへんの話はこのスライドにもまとめてあるので、ぜひ読んでください。


で、こんなイケてるフレームワークを作ったPlayの連中は、やっぱりJavaの世界では異端児なのか、それほど広く受け入れられてるとは言いがたいと思います。特に日本国内では知名度含めて結構絶望的ですし、JavaEEを完全無視しているせいで、キワモノ扱いを受けてますね。


彼らはScalaをサポートし始めました。1.1からScalaプラグインを作ってサポートを始めます。
日本国内では、このへんのエントリをきっかけに、ScalaフレームワークとしてPlay!がプチブレイクし始めた感ありますよね。


そして、Play2.0です。

彼らはついに、Play!をScalaフレームワークとして一から書きなおし始めてしまいました。*3

この判断が正しかったかどうかはまだわかりません。大きな賭けだと思います。ただ、このままJavaのちょっと変わったフレームワークとしての地位に甘んじるよりも、Scalaの未来に賭けよう、という姿勢を僕は支持します。


2012年はPlay!とScalaの年になるでしょう。
"Play2 with Scala in Action"という書籍も出ますし、なによりScalaの総本山であるTypesafeがPlay2.0をテクノロジースタックとしてサポートすると表明しましたから。


という訳で、一日目はPlayの概要についておさらいして見ました。
これだけで終わると寂しいので、1.x系のTipsを幾つか紹介します。

Play! 1.xのTips

  • CookieにSecureフラグを付与する

httpsCookieをやり取りする際は、secureフラグを立てたいのが普通だと思います。
Play!はデフォルトのままだとsecureフラグを自動でたてたりはしてくれませんので、明示的にapplication.confに下記を設定してやる必要があります。

application.session.secure=true

この辺は、私が1年半前にバグ報告した結果付いた機能です。昔はこれも出来なかったんですよね。。
http://d.hatena.ne.jp/ikeike443/20100505/1273040748

  • Memcachedハマリ道(というかapplication.confの癖に注意)

Play!でmemcachedを使うのは簡単で、application.confに下記のように書いてやるだけで動きます。

memcached=enabled
memcached.host=xxxx:11211

memcachedを複数台立てて分散させたい場合は下記のように書くだけです。これだけでPlay!のCache API(の中で使われてるspymemcached)がよしなにしてくれます。

memcached.1.host=xxxx:11211
memcached.2.host=xxxx:11211
memcached.3.host=xxxx:11211
...


ところがちょっと罠があって、例えば開発環境においてはmemcachedサーバは一台しか立ててないけど、ステージング環境では複数台立ててますよ、みたいな状況で例えば下記のように設定してしまうと。。

#開発用
memcached.host=hogedomain:11211
#ステージング用
%QA.memcached.1.host=fugadomain:11211
%QA.memcached.1.host=bazdomain:11211

実はこうしてしまうと、Play!はIDなしの設定(memcached.host=hogedomain:11211)をIDありの設定よりも優先して有効化してしまうという癖がありまして、ステージング環境でもhogedomianのmemcachedを見に行ってしまい、想定外の挙動をしてしまってパニック、ってな感じのハマリ方をします。はい。昔ハマリました。。

なので、解決策としてはIDなしの設定をやめてしまって、開発環境にも明示的にIDを振ってやる、というのが解決策かなーと思います。

つまりこんな感じ。

#開発用
%DEV.memcached.host=hogedomain:11211
#ステージング用
%QA.memcached.1.host=fugadomain:11211
%QA.memcached.1.host=bazdomain:11211


この話、memcachedに限らず、log4jもそうですし、その他の割とあらゆる設定について言えることですので、覚えておくといいと思います。

  • Jobを環境によってOn/Offする

Play!には非同期Jobフレームワークが存在します。
play.jobs.Jobを継承して、@Everyか@Onといったアノテーションを書いておくと、それにしたがってスケジュール実行してくれる便利な機能です。詳しくは以前書いたのでこちらを見てください。

例えばこの機能を使って、あるサービスのAPIを定期的に叩いてデータを収集する、みたいなコードを書いたりするわけです。
プロダクションはそれでいいのですが、自動テストを組んだ時に困るわけです。
実はPlay!のauto-testコマンドの実行時に、Jobも動いてしまうんですよね。

というわけで、環境によってJobをOn/Offできるようにパッチを書いたのです。
http://d.hatena.ne.jp/ikeike443/20100824/1282656188


この機能を使えば、例えばapplication.confに下記のように書いておくだけで、環境によってJobのOn/Offを制御できるわけです。

%DEV.cron.JobA=5min
%DEV.cron.JobB=*    0    *    *    *    ?
%PROD.cron.JobA=5min
%PROD.cron.JobB=*    0    *    *    *    ?
%test.cron.JobA=never
%test.cron.JobB=never

ぜひご活用ください。

  • Dependencies

最後にDependenciesのTipsです。
Dependenciesコマンドを使うと、内部でivyが実行されて、依存関係の解決をやってくれます。
Mavenリポジトリにあるものや、Playのプラグインリポジトリにあるものは暗黙的に解決してくれますし、それ以外のライブラリを解決したい場合は、dependencies.ymlに下記のように書けばいいです。

require:
    - play
    - Some -> SomeLib 1.4
    - Some -> SomeUtil 0.5
repositories:
    - SomeExternalLib:
        type:       http
        artifact:   "http://somedomain/repo/Some/[artifact]/[revision]/[artifact]-[revision].[ext]"
        contains:
            - Some -> SomeLib 1.4
            - Some -> SomeUtil 0.5

ただここで問題なのは、repositoriesのtypeです。
typeをiBiblioにすれば、Maven的に解釈してくれるので(バージョン番号じゃなくてmd5見て変更検知してくれる)、本来はそれでいいんですが、事情によりhttpにしたい場合ありますよね。

httpにしてしまうと、ライブラリに変更が入っても検知してくれないんですね。
というわけでちょっと(かなり?)強引ですが、ivyのキャッシュを強制クリアするオプションを作って取り込んでもらいました。
https://github.com/playframework/play/pull/322


これは次の1.2.4でリリースされると思うので、dependenciesで困ってるひとは活用してもらえると嬉しいです。キャッシュのクリアがかなり強引ですがね。。



以上です。次は@kara_dさんです。僕よりもさらに輪をかけたPlayラバーなので、期待できると思います!!

*1:Promiseが出てきます

*2:Continuationの実装があります

*3:ちなみにJavaも動きます。1.xはJavaで書かれたScalaも動くJavaフレームワーク、だったのが、2.0は逆にScalaで書かれたJavaも動くScalaフレームワーク、になったわけです