play2-fastassetsでPlay2の開発モードを高速化する

Play2の開発モードはなぜ遅いのか?

Play2の開発モードは非常に遅いです。
わかりやすく言うとSeasar2のHOT deployを10倍くらい遅くしたような感じです。これはファイルを編集した際のリコンパイルが遅いというのもあるのですが、編集せずに普通に動かしているだけでもめちゃくちゃ重いです。もちろんアプリケーションの規模にもよりますが、一般的に「大規模」とまでは言えないようなレベルのアプリケーションですら開発していてストレスを感じるレベルです。
これはなぜかというと、そもそも更新のチェックやクラスのリロードなどで1リクエストの処理が遅いということもありますが、リクエストが直列化されて処理されているからというのも大きいと思われます(Play2のソースをちゃんと読んだわけではなく、Seasar2のHOT deployのときの経験と、Play2の挙動からある程度想像で書いてます)。
特に外部CSSJavaScriptファイル、画像ファイルなどを大量に使用しているHTMLの場合、呼び出したコントローラのレスポンス以外にこれらの細かいリソースを取得するためのリクエストが大量に発生しますが、これらが引っかかっていつまでたってもページのロードが終わらない…という状態になります。実際にXMLJSONを返すWeb APIの場合は開発モードで動かしていてもそこまでのストレスは感じません。
ちなみにPlay2は一応ファイルの更新日時を見てファイルが更新されていなかったら302を返してブラウザのキャッシュを使わせるようになっているようですが、そもそもリクエストが飛んでしまう時点で遅くなってしまうのであまり意味がありません。というわけでブラウザにこれらのリソースを積極的にキャッシュさせ、発生するリクエストを減らすことで開発モードの動作を高速化するplay2-fastassetsというユーティリティを作ってみました。

まだMavenリポジトリにpublishしていないので使う場合はリポジトリをcloneして自分でsbt packageしてJARを作るか、ソースは1ファイルだけなのでコピーしてPlay2アプリにそのまま組み込んでいただいてもよいと思います。ある程度安定したらAmaterasのMavenリポジトリにpublishします。

使い方

まずはconf/routesでAssetsを使っているルーティングを以下のように変更します。

#GET /assets/*file controllers.Assets.at(path="/public", file)
GET /assets/*file jp.sf.amateras.play2.fastassets.FastAssets.get(file)

conf/application.confに以下の設定を追加します。

fastassets.urlPath=/assets
fastassets.realPath=/public

Assetsの場合、HTMLテンプレートではroutesからの逆引きでリソースのパスを取得できるのですが(なので別途このような設定を行う必要はない)、FastAssetsではファイル名にタイムスタンプを埋め込むので単純な逆引きができないため、リソースのパスを出力するために専用のヘルパーを使用します。なのでそのヘルパー用にこのような設定が必要になってしまっています。いけてないですが、他にうまいやり方があるのかな…?
HTMLテンプレートでは以下のような感じでFastAssets.atというヘルパーを使用してリソースのパスを取得します。

@(title: String)(content: Html)
@import jp.sf.amateras.play2.fastassets.FastAssets
<!DOCTYPE html>
<html>
  <head>
    <title>@title</title>
    <link rel="stylesheet" media="screen" href="@FastAssets.at("stylesheets/main.css")">
    <link rel="shortcut icon" type="image/png" href="@FastAssets.at("images/favicon.png")">
    <script src="@FastAssets.at("javascripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
  </head>
  <body>
    @content
  </body>
</html>

FastAssetsはリクエストされたリソースに対してキャッシュを促すヘッダを付けてレスポンスを返すので、次回からキャッシュの有効期限が切れるまでの間はこれらのリソースに対してはサーバへのリクエストは発生せず、ブラウザのキャッシュが利用されるようになります。ただしファイルが編集されるとファイル名に埋め込まれるタイムスタンプが変更されるため、再度サーバにリソースを取得しに行きます。なので「ファイルを更新したのにキャッシュが使われて反映されない!」というトラブルも防げるはずです。たぶん。

注意点

FastAssets.atで参照するリソースはいいのですが、CSSJavaScriptなどの中から参照しているリソースがある場合、タイムスタンプの埋め込みができないので一度キャッシュされるとファイルを変更しても反映されないことがありそうな気がします。FastAssets.atで出力したパス以外の場合はキャッシュはしないようにしたほうがよいかもしれません。