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フレームワーク、になったわけです

第二回 Playframework 勉強会 in Tokyo やりました #play_ja

10月8日にPlayframework勉強会をやりました。
http://atnd.org/events/19107


当初は30人規模の会場でやろうとしていたんですが、あっという間に定員を倍以上オーバーしてしまったため、急遽100人規模の会議室を借りて開催しました。
ニフティさんにも打診したのですが、既に同日は埋まってたので、お金を出して借りることになりました。。

そんな感じで困っていたところに、気前よく資金提供いただいた株式会社イージフ様と株式会社シャノン様には心から感謝します。
イージフ様は来週の木曜、ビックサイトで行われる「eドキュメントJAPAN2011」に、AlfrescoのCTOを招聘して、「オープンソースの視点から見るECMのグローバル市場」という題名で出展されるそうです。
ご興味のある方はぜひ足をお運びください。


勉強会を主催するのって、会場押さえたり設営したり懇親会の場所押さえたり、結構大変でしたが、やってよかったなと感じることが出来ました。
こうやって勉強会をやってみると、意外にPlayframeworkが好きな人、使ってる人が多いことに気づけて、とても心強い感覚を得られたのが収穫でした。


運営を手伝ってくれたシャノンのゆかいな仲間たち(@fujya, @yosu, @twintaro)、それから早めに来て手伝ってくれた@tan_go238さん、ありがとうございました!


さて、勉強会の発表内容について、下記に報告します。

Play! の紹介

一発目は私の発表でした。
内容的には、Playframeworkについてよく知らない人を対象に、特徴を30分で紹介するというものです。
Play! についてよく知ってもらうには実際にコードを書いて動かしてみせるのが一番いいだろう、ということでライブコーディングをやりました。
詳しくはスライドとUstreamの録画をご覧ください。
...と言いたかったのですが、ミスでこの部分は録画できていません。。さーせん。。


大体下記のようなことを紹介しました。

  • Play! はJavaEEの規約ではなく、Webのアーキテクチャに従う
  • シェアードナッシング、非同期I/Oといった特徴を持つ
  • Kloutや英ガーディアンなどが使っている
  • SASSやCoffieScript、MongoDBなどのプラグインがあり、新しいトピックへの反応が速い
  • HerokuやCloudbees、GAEなど、あらゆるPaaSにデプロイ出来る
  • Play2.0でさらに進化する

ライブコーディングの内容は下記内容のものを解説しながら10分で作る、というものでした。

  • DBアプリ
  • 管理画面付き
  • JSONを返すREST API付き

ちょっと失敗したんですがw 
現場での雰囲気から推し量るに、このライブコーディングで伝えたかったPlay! の生産性の高さと分かりやすさについては、伝わったっぽいです。


と、いろいろ喋ったんですが、一番伝えたかったことを要約すると、下記の1行に尽きます。
JavaでWeb開発するのって、本来こんなに楽しいものなんだよ!!!


ぜひ資料をご覧ください。


playdocja のこれまでとこれから

ここからはUstの録画つきです。(途中からですが。。)

@garbagetown さんは、Play!が出始めた初期の頃に日本語翻訳サイト(playdocja)を立ち上げて翻訳をしてくださっている方です。
私もすこーしだけ協力していますが、翻訳の90%以上は@garbagetownさんによるものと言っても過言ではないです。
いつもお世話になっております。


発表資料がアップされていないのでここに貼れていませんが(10月13日追記:アップされました)、内容としては下記でした。

  • playdocjaを立ち上げたきっかけは、Play!内部に組み込まれているDocViewerプラグインに気づいたこと
  • 今回は翻訳の仕方をご紹介して、みんなを翻訳者として勧誘したいということw
  • playdocjaのホスト先であるGAEが値上げしたのでHerokuあたりに引っ越そうと考えていること

これを読んでいるみなさんはもちろん、翻訳協力してくれますよね?
興味のある方は@garbagetownさんに直接Twitterで話しかけるか、play_jaのMLに投稿頂けるといいかなーと思います。


play_jaで独自ドメイン取る? って話もありましたが、ちょっと本当に検討したいですねー。

Play!で作る業務アプリケーション構成例

@genki_ さんによる、Play!で業務アプリを作っていますよ、っていうおはなし。

資料は下記です。
「業務で使うPlay!」


私も業務でPlay!を使っていますが、それは私が所属しているのが自社サービスをやっている会社だからできるのであって、SI案件でPlay!を提案して通したって言うのはすごいなーと思います。

Play!を使うとAOPも結構簡単に書けるし、業務アプリ開発にも全く問題ないよー、っていう発表でした。
これは参考になった方多かったのではないでしょうか?


また、@genki_さんは発表をiPadのkeynoteでされていて、iPadの画面を指でグリグリ触ってポインタを動かしていたんですね。
それもとても注目されていました。
iPadのkeynoteにあんな機能があるなんて知らなかった。。
そのへんも注目して御覧ください。


Play! + GAEで作ったアプリをPlay! + Heroku で動かすとどうなるか

@hagikuratakeshiさんによる発表。

Play!+GAEで作ったサービスを、Herokuに移行してみたよーっていうタイムリーでおもろい話でした。

みんなGAEの値上げには困っているみたいですね。。
Googleさんも商売なので値上げ自体はしょうがないと思うのですが、ベンダーロックインでこの価格帯だと、正直割りに合わないよねー、っていうのが会場とTLの雰囲気でしたね。。


@hagikuratakeshiさんの発表が面白いのは、ただHerokuに移行したよーだけで話が終わらず(これだけで充分興味深いんだけど)、MongoDBも使ってみたよ、Sienaで上手く動かなかったからSienaのMongoDB用PersistenceManagerを書いて動くようにしたよ、っていうところですよね。

Siena-MongoDBが本家にマージされることを期待して待ってます!

LT

LTもやりました。5分×6人で30分を予定していたのですが、一人分枠が余ったので、最後に私もLTしましたw

大阪勉強会の続きのはなし

大阪勉強会でPlay! on GAEについて発表してくれた@daiksyさんが、なんと上京してくれました!!

また大阪でもやりましょう!

Play FrameworkでWebSocketを使ってSVGのビジュアルチャットを作ってGDD open call html5に応募してみたけど落ちました

@kara_dさんによるPlay!+WebSocketの話。
電車のなかで設計してラウンジオフィスで開発して帰りの電車でデプロイした、みたいな話をさらっとするあたりかっこいいすね!

WebSocketの仕様の変わりっぷりとか、Chromeのバージョンアップの勝手っぷりとかを軽くDisってましたが、私も共感しましたw

残念ながら5分で終わりませんでしたが、とても面白いLTでした。

Play!+WebSocketハッカソンを近々やる予定みたいです。
参加してみたいなー。

HerokuのPlay! 対応について

セールスフォース社のエバンジェリストである、@mitsuhiro 岡本充洋さんもいらしてくれました。
LT枠で、Heorkuについてかっこ良くプレゼンしてくれました。

資料の洗練されっぷりにみな圧倒されていました。

Herokuはとても開発しやすくて、いいPaaSだよなーと思いましたね。

5分で説明するPlay-Scala

@masahitoさんによる、Play! Scalaの紹介。
今回の勉強会ではScalaに関する言及が少なかったのですが、@masahitoさんがやってくれました。
開発の仕方から、Anormなどのモジュールの説明まで、分かりやすく紹介してくれました。

NettyはPlay!でどう使われてるのか的な話

@tan_go238さんによる、Play!内部でのNettyの使われ方に関するLT。
@tan_go238さんも大阪の勉強会の続きでLTしてくれました!
Play!の内部では、NettyというJBoss由来の非同期I/Oライブラリを使っているのですが、そいつがPlay!内部でどう使われているかについて、さらっと分かりやすく説明してくれました。

この辺のコードを読むとすごく面白いので、ソースコードリーディングもやりたいですね!て話を懇親会でしましたw
やりたいなあ。

Jenkins+Play!で気軽にCI

最後に、枠が余ってたので私がしゃべりました。
今年2月ごろにJenkins勉強会でLTした内容をもう一回しゃべりました。
Jenkins勉強会でやったときは5分で終わらなかったので、リベンジですw

ちなみに、見事5分以内にしゃべり終わりました!
やったね!!

懇親会

私は腸を痛めていたので飲めませんでしたが、私以外の皆さんはとても楽しく飲んでいましたw
いや、私もウーロン茶で楽しく語り合えてとてもよかったです。

Play!に興味を持っている人、既にぞっこんLOVEな人、いろんな方がいらっしゃいました。

とっても楽しかったです。

次回は?

次回はいつやりましょうかね。
2,3ヶ月後にはやりたいなーとは思ってるんですが。
発表形式でやるか、ソースコードリーディングやるか、または他のことやるか。

続きはMLで話しましょう!

また一緒にPlay!しましょう!!

Playframework勉強会やります

今週の土曜日に、Playframework勉強会を執り行ないます。
http://atnd.org/events/19107

あいにく既に満席ですが、当日になってキャンセルされる方がでる可能性もあります。

興味が有る方はとりあえず補欠登録してみてください。当日来てみたら入れた、って可能性は充分あります。


当日来場が難しい方は、Ustで御覧ください。

下記がUst放送予定地です。
http://www.ustream.tv/channel/playframework%E5%8B%89%E5%BC%B7%E4%BC%9A


SalesForceエバンジェリストの方もいらしてPlay on HerokuについてLTでお話ししてくれますし、楽しい勉強会になるといいなと思います。

Playframework 1.2 雑感

なんだかすっかり遅くなっちゃったけど、Play1.2がリリースされたので、リリースノートを読みながら確認をば。

1.2 概要

全体的に、意欲的なアップデートと言えそう。
dependenciesコマンドの追加、テスト時のsurefireの出力サポート、等々、やっとCI周りがまともになり、さらにDB管理機構のEvolutionが++な感じ。
またこのバージョンからWebSocketが正式にサポートされた。
他にも非同期HTTP通信に関する意欲的な機能が追加されており、次世代な感じが満載。

依存関係管理機能がやっと追加

PlayはMaven非対応。なぜならGuillaumeがMaven嫌いだから。
MLでMavenで依存関係管理したい、っていう問い合わせがあるたび、ivy使えば、と突き放してきたわけですが、1.2でやっと機能が追加されました。
dependenciesコマンドです。
これはMavenリポジトリに配置されたJarの依存関係の解決と、Playモジュールの依存関係の解決の両方をやってくれるようです。
ちなみに内部ではivyが使われてます


dependency.yml というファイルがconfの下に追加されています。こいつに依存関係を下記のように記述してやって、

# Application dependencies
transitiveDependencies: true 

require:
    - play
    - play -> crud
    - play -> secure
    - commons-httpclient -> commons-httpclient 3.1

下記コマンドを実行してやればいいです。

$ play dependencies

するとlibの下にcommons-httpclientが入り、Playモジュールであるcrudとsecureがインストールされます。


独自リポジトリに配置されたjarを使いたい場合には例えば下記のように書きます。

require:
    - play
    - SsApi -> SsApiCommon 0.0.9
repositories:
    - Shanon:
        type:       http
        artifact:   "http://path/to/mymavenrepo/SsApi/[artifact]/[revision]/[artifact]-[revision].[ext]"
        contains:
            - SsApi -> SsApiCommon 0.0.9

こうすると、僕の所属する株式会社シャノンのリポジトリからSsApiCommon-0.0.9.jarというファイルを取ってきて、libに配置してくれます。


詳細はここを参照してください。

Surefire レポートが出るようになった

以前、Jenkins勉強会でPlayのCI事情についてLTしたときには、PlayはSurefireを書きだしてくれていませんでした。
独自のHTMLフォーマットで結果を書き出すのみだったので、少しでもJenkinsで使いやすくなるよう、プラグインを書いたわけですが、この問題も解決されました。
1.2からは、テスト実行後にSurefireを書きだしてくれるようになったので、テスト結果の集計も出来るようになり、JenkinsのようなCIサーバとの相性がグッとよくなったと思います。

バグ注意!

ちなみに、このsurefire出力にはバグがあります。*1
テストがこけると、まともなxmlを書き出せなくなってしまうので、Jenkinsなどでテスト結果を集計する最中に例外を吐きます。。
既に次の1.2.1には修正パッチが適用されていますが、1.2の段階ではバグが残っているので注意してください。

フィクスチャYAMLの中でテンプレートタグが使えるようになった

Playframeworkには、元々フィクスチャの機能があります。Railsなんかでよく見るアレですね。
テスト用にデータをロードしてくれるアレです。


1.2から、フィクスチャYAMLの中にテンプレートタグが使えるようになったので、テスト用にデータを用意するのが大分楽になったと思います。
例えば下記のようなデータを用意したい時に、

vendor.tag.AreaTag(at11):
    label:            China
vendor.tag.AreaTag(at12):
    label:            France
vendor.tag.FunctionTag(ft1):
    label:            Wedding
vendor.tag.FunctionTag(ft2):
    label:            Party

いちいち複数行に分けて書かずに、タグを使ってこう書けます。

#{list ['China', 'France']}
vendor.tag.AreaTag(at1${_index}):
    label:${_}
#{/list}
#{list ['Wedding', 'Party']}
vendor.tag.FunctionTag(ft${_index}):
    label:${_}
#{/list}

大量データのテストを書かないといけない場合などに重宝しそうですね。


上記の他にも、Playで使えるあらゆるタグが使える(つまりGroovyがほぼそのまま使えるということでもある)ので、工夫次第でかなりのことができそうです。

DB変更管理が可能になった

Railsでいうマイグレーションに当たる機能になると思いますが、Evolutionという機能が追加されました。
DDLの変更の追跡、コンフリクトの解決、等々を面倒みてくれる機能のようです。
JPAを使っていれば、開発中はauto-updateの機能を使って自動的にスキーマを変更してしまうのでこの機能はいらないのですが、プロダクション環境にデプロイする段になると、必要になる機能だと思います。


使い方のイメージとしては、下記のような感じでしょうか。

  • 開発中はapplication.confにddl=updateと書いておいて、スキーマを自動更新
  • リリースの際に、dbモジュールを使ってddlを抜き出し、db/evolutionsに配置しておく
  • プロダクション環境においてevolutionsコマンドを実行し、スキーマを変更する

今まではdbモジュールを使ってddlを抜き出した後は手動で変更管理をしていたので、便利に使えそうです。

バグ注意!

尚、このevolutionsコマンドもバグがあります。
crudやsecureのようなPlayモジュールをインストールしている場合、Null Pointer Exceptionを吐いて落ちてしまいます。。
僕が見つけたのでチケット起票し、pull requestしておきました。
既にマージされたので、1.2.1からは治っているでしょう。

WebSocketサポート、非同期HTTPサポート

WebSocketが正式にサポートされたのはとても大きいですね。まだチェックしきれてないのですが、期待大です。

また、1.1から引き続き、非同期HTTPに関する実装が追加されています。
なにが行われているかを理解するために、今play.classloding.enhancersパッケージのコードを読みながら確認してる最中なので、詳細はアレですが。。

なんか、長いリクエストによってスレッドプールが占有されないように、一回サスペンドしておいて処理が終わってから改めてレスポンスを返すようにする、みたいなことをやってるっぽいです。

また同時にチャンクレスポンスにも対応してるので、例えば下記のようなコードで、大きなサイズのファイルダウンロードに関する問題を解決することができるんじゃないかと、個人的に期待しています。

public static void generateLargeCSV() {
    CSVGenerator generator = new CSVGenerator();
    response.contentType = "text/csv";
    while(generator.hasMoreData()) {
          String someCsvData = await(generator.nextDataChunk());
          response.writeChunk(someCsvData);
    }
}

詳細はここここを是非参照してください。

非同期HTTPやWebSocketについては、もう少し理解が深まったら改めてブログに書きたいと思います。

Play Scalaが独立したサイトを持つようになった

これです。
いよいよ本気でScalaフレームワークとしてブレイクしそうですね!


いやーなかなか、Play!は面白くなってきましたなあ!

*1:[http://play.lighthouseapp.com/projects/57987/tickets/744-testrunner-create-surefire-reports-containing-malformed-xml:title=このチケットを参照]

IntelliJ IDEA で Playframwork の Scala モジュールを動かした

Play ScalaでWebアプリ書きたいっす。

っていうか多分来年、社内アプリとしてScala使って書くっす。なんでわざわざScalaで、とかは聞かないで。
フレームワークはLiftじゃなくてPlayを使うっす。なんでそんなマイナーなのを、とか聞かれても特に答えとか持ってないですよ。
使いたいからです。

で、僕だめグラマーなので、オートコンプリート機能がないとコードかけないんだけど、EclipseのScalaIDEはなんだか重いし、まともに補完出来ないし、だめじゃね、ってことで。

Eclipse以外の選択肢の検討

vim

まずvim+codefellowでやろうとして挫折。
スクリーンキャスト見る限りとってもよさそうなんだけど、vimをビルドしなおしたりといろいろ面倒だし動かし方が結局よくわからなかったので挫折。。

NetBeans

NetBeansいいと聞いて、この辺とかこの辺を参考に設定してPlay Scalaが動くところまで確認。
補完も上手く動くし、デバッグもできるし、大分いいです。
動かし方はリンク先を見れば特に問題なくやれると思うので、特に書きません。

IntelliJ IDEA

その後にTwittershin1さんpomuさんにIntelliJがいいよ、って教えてもらったのでIntelliJでPlay Scalaを動かせるようにしようかなと思った次第。
以下に簡単に動かし方を書いておきます。

Play Scala を IntelliJ IDEA で動かすには

まずPlay Scalaのセットアップ

何はなくともまずPlay1.1をインストールして下さい。
Play1.1は最近正式にLaunchpadからgithubに移行されました。
githubに置いてあるREADMEを見ればわかると思います。
https://github.com/playframework/play

PlayをインストールしたらScalaモジュールを追加でインストールします。

$ play install scala
~        _            _ 
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/   
~
~ play! 1.1RC2-90-g0f0c5cb, http://www.playframework.org
~
~ Will install scala-0.8
~ This module is compatible with: Play-1.1
~ Do you want to install this version (y/n)? 

次に下記コマンドでPlay Scalaプロジェクトの作成をしておきます。

$ play new myapp --with scala

ここまでで既にPlay Scalaは動きます。
下記コマンドで動作を確認しておきましょう。

$ cd myapp
$ play run
~        _            _ 
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/   
~
~ play! 1.1RC2-90-g0f0c5cb, http://www.playframework.org
~
~ Ctrl+C to stop
~ 
Listening for transport dt_socket at address: 8000
12:48:17,515 INFO  ~ Starting /Users/ikeike443/IdeaProjects/myapp
12:48:17,520 INFO  ~ Module scala is available (/Users/ikeike443/play/modules/scala-0.8)
12:48:17,810 WARN  ~ You're running Play! in DEV mode
12:48:17,953 INFO  ~ Listening for HTTP on port 9000 (Waiting a first request to start) ...

IntelliJ IDEAのセットアップ

次にIntelliJ IDEA 本体のインストールをします。
ここからCommunity Editionをインストールしましょう。

次にIntelliJのScalaプラグインをインストールします。
この辺を参考に。
Setting up the Environmentってところを読めばいいと思います。

PlayとIntelliJを統合する

Playには、標準でIntelliJに対応したセットアップコマンドが用意されています。
下記のように実行しましょう。これで、myapp.iml というファイルが生成されます

$ play idea
~        _            _ 
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/   
~
~ play! 1.1RC2-90-g0f0c5cb, http://www.playframework.org
~
~ OK, the application is ready for Intellij Idea
~ Use File/New Module/Import Existing module
~

出来上がったimlファイルをIntelliJにインポートします。
playが出力してくれているとおり、メニューを
File/New Module/Import Existing module
と辿って、imlファイルを指定します。

これでIntelliJ上でPlayのプロジェクトを編集できるようになりました。

ところが、Eclipseであれば、Playコマンド(play ec)だけでlaunchファイルも作られて全て済むのでここで終わるはずなんだけど、IntelliJはGuillaume自身も使ってないせいか、なんか適当な扱い。。
play ideaだけじゃ動きません。

実行とデバッグ、テスト実行のための設定をしましょう

IntelliJのメニューから、
Run/Edit Configurations
と辿って、Applicationを選択し下記パラメータを設定します。

Main class:
play.server.Server 
VM parameters:
 -Dapplication.path=/Users/ikeike443/IdeaProjects/playidea
 -Djavaagent=/Users/ikeike443/play/framework/play.jar
 -Dplay.debug=yes
 -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
 -Xdebug
 -classpath /Users/ikeike443/IdeaProjects/playidea/conf:/Users/ikeike443/play/framework/play.jar:/Users/ikeike443/play/modules/scala-0.8/lib/jline.jar:/Users/ikeike443/play/modules/scala-0.8/lib/play-scala.jar:/Users/ikeike443/play/modules/scala-0.8/lib/scala-compiler.jar:/Users/ikeike443/play/modules/scala-0.8/lib/scala-library.jar:/Users/ikeike443/play/modules/scala-0.8/lib/scalatest-1.2-for-scala-2.8.0.RC7-SNAPSHOT.jar:/Users/ikeike443/play/framework/lib/activation-1.1.1.jar:/Users/ikeike443/play/framework/lib/antlr-2.7.6.jar:/Users/ikeike443/play/framework/lib/async-http-client-1.3.0.jar:/Users/ikeike443/play/framework/lib/bcprov-jdk15-145.jar:/Users/ikeike443/play/framework/lib/c3p0-0.9.1.2.jar:/Users/ikeike443/play/framework/lib/cglib-2.2.jar:/Users/ikeike443/play/framework/lib/commons-beanutils-1.8.3.jar:/Users/ikeike443/play/framework/lib/commons-codec-1.4.jar:/Users/ikeike443/play/framework/lib/commons-collections-3.1.jar:/Users/ikeike443/play/framework/lib/commons-email-1.2.jar:/Users/ikeike443/play/framework/lib/commons-fileupload-1.2.jar:/Users/ikeike443/play/framework/lib/commons-io-1.4.jar:/Users/ikeike443/play/framework/lib/commons-lang-2.5.jar:/Users/ikeike443/play/framework/lib/commons-logging-1.1.1.jar:/Users/ikeike443/play/framework/lib/dom4j-1.6.1.jar:/Users/ikeike443/play/framework/lib/ehcache-core-2.0.0.jar:/Users/ikeike443/play/framework/lib/ezmorph-1.0.3.jar:/Users/ikeike443/play/framework/lib/groovy-all-1.7.1.jar:/Users/ikeike443/play/framework/lib/gson-1.4.jar:/Users/ikeike443/play/framework/lib/hibernate-annotations-3.5.6-Final.jar:/Users/ikeike443/play/framework/lib/hibernate-commons-annotations-3.2.0.Final.jar:/Users/ikeike443/play/framework/lib/hibernate-core-3.5.6-Final-patched.jar:/Users/ikeike443/play/framework/lib/hibernate-entitymanager-3.5.6-Final.jar:/Users/ikeike443/play/framework/lib/hibernate-jpa-2.0-api-1.0.0.Final.jar:/Users/ikeike443/play/framework/lib/hsqldb-1.8.1.2.jar:/Users/ikeike443/play/framework/lib/jamon-2.7.jar:/Users/ikeike443/play/framework/lib/javamail-1.4.3.jar:/Users/ikeike443/play/framework/lib/javassist-3.9.0.GA.jar:/Users/ikeike443/play/framework/lib/jaxen-1.1.jar:/Users/ikeike443/play/framework/lib/jj-imaging.jar:/Users/ikeike443/play/framework/lib/jj-simplecaptcha.jar:/Users/ikeike443/play/framework/lib/jj-textile.jar:/Users/ikeike443/play/framework/lib/jj-wikitext.jar:/Users/ikeike443/play/framework/lib/jodatime-1.6.jar:/Users/ikeike443/play/framework/lib/jregex-1.2_01.jar:/Users/ikeike443/play/framework/lib/jsr107cache-1.0.jar:/Users/ikeike443/play/framework/lib/jta-1.1.jar:/Users/ikeike443/play/framework/lib/junit-4.8.1.jar:/Users/ikeike443/play/framework/lib/log4j-1.2.15.jar:/Users/ikeike443/play/framework/lib/memcached-2.4.2.jar:/Users/ikeike443/play/framework/lib/mysql-connector-java-5.1.13-bin.jar:/Users/ikeike443/play/framework/lib/netty-3.2.2.Final.jar:/Users/ikeike443/play/framework/lib/org.eclipse.jdt.core_3.6.0.jar:/Users/ikeike443/play/framework/lib/oval-1.50.jar:/Users/ikeike443/play/framework/lib/provided-servlet-2.5.jar:/Users/ikeike443/play/framework/lib/signpost-core-1.2.jar:/Users/ikeike443/play/framework/lib/slf4j-api-1.6.1.jar:/Users/ikeike443/play/framework/lib/slf4j-log4j12-1.6.1.jar:/Users/ikeike443/play/framework/lib/snakeyaml-1.6.jar:/Users/ikeike443/play/framework/lib/xstream-1.3.jar
Use classpath and JDK of module:
自分のプロジェクト(myapp)

要はPlayの実行に必要なjarすべてに対してclasspathを明示的に通してやればいいです。

尚、Playの機能の一つであるテストモード実行をIntelliJでやりたい場合はテスト実行用の設定を別途コピーして作り、上記に加えてtestrunnerもclasspathに通してやればいいです。

#上記に加えて下記を追加
/Users/ikeike443/play/modules/scala-0.8/lib/scalatest-1.2-for-scala-2.8.0.RC7-SNAPSHOT.jar:/Users/ikeike443/play/modules/testrunner/lib/play-testrunner.jar

デバッグは8000番ポートに接続するようにしてやればいいので、
Run/Edit Configurations
からRemoteを選択して、下記のように設定すればいいです。

Transport: Socket
Debugger mode: Attach
Host: localhost
Port: 8000

動作を確認しましょう

これでIntelliJの上でPlay Scalaを実行、デバッグ、テスト実行ができるようになりました。
IntelliJからPlayを実行して、ブラウザからlocalhost:9000を確認して下さい。
下記のような画面が出力されていればOKです。
f:id:ikeike443:20101227135030p:image
テスト実行もできます。テスト画面はlocalhost:9000/@testsから確認して下さい。
f:id:ikeike443:20101227135031p:image
デバッグもちゃんと動きます。いい感じですね!
f:id:ikeike443:20101227135032p:image

でもまだIntelliJのことはよくわからない。。

まだIntelliJの仕組みがよくわからんけど、上記のパラメータをPlay ideaコマンド実行時に全て自動でつくりにいきたいな。。
IntelliJってその手の情報はどこに持ってるんだろう?
Play ecだとEclipseフォルダの下にlaunchファイルを作ってくれるんだけど、そういうのやりたい。

まずはIntelliJと仲良くなってからかな。。

次はPlayのテストフレームワークとAuto-Testについて書こうかなと思います。。その前にPlay自体についてもう少し紹介記事とか書いたほうがいいのかなとも思うけど。。

追記

自分も翻訳に協力していながら、このページの存在を忘れてました。
Eclipse、NetBeans、IntelliJ IDEA、TextMate、それぞれのIDEを使う際の設定の仕方など参考になるので、ここも参考にしてください。

xmemcachedというクライアントがKestrelをサポートしているらしい

http://code.google.com/p/xmemcached/wiki/Getting_Started#Talk_with_Kestrel

Kestrelはflagをセットしても勝手に無視しちゃうので、flagに相当する値をflagではなくvalueの先頭4バイトに押し込むことで対応しているようだ。
ちょっと強引だけど、使えるのかなー。

Kestrelはflagを無視する

Kestrel ignores memcache flags
チケットとしても起票されてたみたいだけど、その後特に対応することもなく閉じられたみたい。
Twitter内部ではflagを使ってないんでしょうね。。

Scalaを学習して改修するか、バイト配列のまんまでやり取りするしかないんか。。

んー、バイト配列のまんまは危険なのでやなんだけど、例えばMemcachedClientの方に少し手を入れて、StringとString以外のオブジェクトをサポート出来れば、プリミティブ型は全部諦めればいいかなー。。。
こんな感じ? ↓
http://github.com/ikeike443/Memcached-Java-Client/commit/60c88eaa4880bc9275027bd1fbac9b493208de08#commitcomment-116465

で、こんなふうに使う?

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import com.meetup.memcached.MemcachedClient;
import com.meetup.memcached.SockIOPool;

public class KestrelTest {
	public static void main(String[] args) throws ClassNotFoundException, IOException{
		SockIOPool pool = SockIOPool.getInstance();
		pool.setServers(new String[]{"localhost:22133"});
		pool.initialize();

		MemcachedClient mcc = new MemcachedClient();

		MyObject myObject = new MyObject();
		mcc.set("name", "aho");
		mcc.set("name", "baka");
		mcc.set("obj",myObject);

		String name = (String) mcc.get("name",null,true);
		System.out.println(name);
		String name2 = (String) mcc.get("name",null,true);
		System.out.println(name2);
		byte[] obj = (byte[]) mcc.get("obj");
		ObjectInputStream inObject = new ObjectInputStream(new ByteArrayInputStream( obj));
		MyObject readObject = (MyObject) inObject.readObject();
		System.out.println(readObject.toString());
	}
}

やだな。。せめてdecodeをいじるか。。