Play framework with Scala を使ってみようシリーズです。
その1
その2
その3
その4
その5
先日、Play! 1.1.1 がリリースされました。
今回のリリースはバグフィックスとのことですので、大きな機能追加はないようです。
開発者MLによると、3月に1.2のリリースを予定しているという話がありました。
さて、今回はPlay! on GAE with ScalaでMemcacheを使ってみます。
Play!には標準でキャッシュのためのAPIが備わっていますが、GAEモジュールを利用することでGAEのmemcacheにデータをキャッシュすることができます。
今回のサンプルのソースは https://github.com/ueshin/play-hello/tree/play-hello-0.0.4 でブラウズできます。
Play!本体とsienaモジュールをアップデート(play-1.1.1、siena-1.4)してありますので適宜インストールもしくは読み替えをして下さい。
その1
その2
その3
その4
その5
先日、Play! 1.1.1 がリリースされました。
今回のリリースはバグフィックスとのことですので、大きな機能追加はないようです。
開発者MLによると、3月に1.2のリリースを予定しているという話がありました。
さて、今回はPlay! on GAE with ScalaでMemcacheを使ってみます。
Play!には標準でキャッシュのためのAPIが備わっていますが、GAEモジュールを利用することでGAEのmemcacheにデータをキャッシュすることができます。
今回のサンプルのソースは https://github.com/ueshin/play-hello/tree/play-hello-0.0.4 でブラウズできます。
Play!本体とsienaモジュールをアップデート(play-1.1.1、siena-1.4)してありますので適宜インストールもしくは読み替えをして下さい。
Scalaモジュールの修正
Play! on GAE with Scalaのキャッシュ機能には次の2つの不具合があるため、現在提供されているPlay!、およびScalaモジュールでは正しく動作しません。(2011.01.29現在)- Scalaモジュールの
ScalaCache
が、Actor
を起動しようとする。
-> GAEではThread
が利用できない(必然的にActor
も利用できない)ため、エラーとなってしまう。
-> github上では修正済みですが、まだリリースされていません。 - Play!起動時の初期化フローが正しくない(と思われる)ため、
GAECache
を利用するよう設定されない。
-> Java版でも同様の現象となります。
-> Play! 1.1.1 でも修正されていませんでした。 => ticket #527
上記2点は、Scalaモジュール0.8を少しだけ修正することで回避できます。
既存のScalaモジュールをコピーする
コピー先のバージョン指定はなんでもいいですし、コピーせずにそのまま修正でも構わないです。指定したバージョンは
application.conf
ファイルに反映してください。$ cd ${PLAY_HOME}/modules
$ cp -R scala-0.8 scala-0.8.1
scala-0.8.1/src/play/cache/ScalaCache.scalaを修正
15行目をlazyにする。 13 private def prefixed(key: String) = "__" + key
14
15 private lazy val cacheActor =
16 actor{
17 link{self.trapExit = true;loop{react{case Exit(from: Actor, exc: Exception) =>
Actor
を遅延評価にすることで、勝手にActor
が起動するのを防いでいます。これで1点目の修正ができました。
scala-0.8.1/src/play/cache/CacheDelegate.javaを修正
14行目あたりにstatic
初期化ブロックを作ってCache
の初期化をする。 12 abstract class CacheDelegate {
13
14 static { Cache.init(); }
15
16 public void add(String key, Object value, String expiration) {
17 Cache.add(key, value, expiration);
18 }
Cache
の初期化はPlay!起動時に行われるんですが、GAECache
が設定される前に初期化されてしまうので、ここで再度初期化してあげます。初期化をここですべきかどうかはアレですが(w)、暫定的に、ということで、本対応を待ちましょう。
Scalaモジュールをビルド
同梱されているbuild.xmlファイルのままではビルドが成功しないので、8行目を修正します。 7 <path id="project.classpath">
8 <pathelement path="${play.path}/framework/play.jar"/>
9 <fileset dir="${play.path}/framework/lib">
ant
コマンドでビルドします。$ ant -Dplay.path=${PLAY_HOME}
ビルドが正常に完了したらオッケーです。
application.conf
ファイルのScalaモジュールのバージョンを新しく作成したバージョンに合わせてください。Cache
を使う
キャッシュするには、play.cache.Cache
クラス(実体はplay.cache.ScalaCache
クラス)を使います。Java版で提供されている各種メソッドも利用できますが、今回はScala版特有の機能を利用しました。
Application.scala
53 val(followings, users, cachedAt) = Cache.get(followingsCacheName(currentUser.email), "10min") {
54 val followings = Follow.followings(currentUser)
55 val users = followings.flatMap(f=> User.get(f.following).map(user=>(f->user))).toMap
56 (followings, users, new Date)
57 }
これは、
Option#getOrElse
のような動作になります。要するに、キャッシュが存在したらそれを使い、なければ後続のブロックが処理された結果を新たにキャッシュして返す、というわけです。
キャッシュのためのキーを第1引数、存続時間が第2引数となります。
キャッシュ対象となるインスタンスのクラスは
Serializable
である(@serializable
アノテーションが付いている)必要があります。(=> User.scala / Follow.scala)簡単ですね!
ちなみにGAEでない場合には、サーバーのメモリ上か、Memcachedをキャッシュとして利用します。
また、Memcachedと互換のあるプロトコルを実装しているもの(kumofsやMembaseなど)であればキャッシュ先として利用可能です。(・・・と思います。すいません、試してません。。。)
サンプルアプリ
実際にGAE上で動作しているサンプルアプリを公開しています。ログインして、Followings画面に表示される一覧をキャッシュに登録しています。
保持期間は10分、もしくは新たにフォローするまで、となっています。
いつになっても見た目がしょぼいのでどなたかがデザインをあててくれる事を切に願います。