Documentation

You are viewing the documentation for the 2.1.x release series. The latest stable release series is 2.4.x.

§Play 2.1 の変更点

§Scala 2.10 への移行

Play の全てのランタイム API がScala 2.10 に移行しました。これにより、Scala 最新版に含まれる素晴らしい機能を使ってアプリケーション開発が行えるようになりました。

同時に ビルドシステム (sbt) の Scala バージョンと、アプリケーション実行時に利用される Scala のバージョンを独立させました。これにより、 実験的または試験的なブランチの Scala を使ってより簡単に Play のビルドやテストを行えるようになりました。

§scala.concurrent.Future への移行

Scala 2.10 で導入された目玉機能の一つに、Scala で非同期コードを扱うための scala.concurrent.Future というライブラリがあります。Play 自身、この API をベースに動作するようになりました。また、Play の非同期 HTTP 通信やストリーミングの機能も同 API で直接扱えるようになりました。

さらに Play と Akka 、またこれからリリースされるであろう非同期なデータストアドライバとを今まで以上に容易に組み合わせられるようになりました。

同時に、実行コンテキストのモデルがよりシンプル化されるように見直しを行った結果、アプリケーションの部分部分で非同期コードを実行する ExecutionContext を容易に選択できるようになりました。

§モジュール化

Play プロジェクトがいくつかのサブプロジェクトに分割されて、その中からユーザ自身が必要最小限のものを選択できるようになりました。

今後、アプリケーションに必要だと思われる依存プロジェクトを次のリストの中から自由に選択することができます。

play コアプロジェクトには非常に限られた依存ライブラリしか含まれていません。そして、コアはそれ単体で最小構成の非同期的、かつハイパフォーマンスな HTTP サーバとして利用できます。

§アプリケーションのより柔軟なモジュール化

Play 2.1 に追加されたサブ・ルータ合成という機能のおかげで、アプリケーションの部分部分をこれまでより柔軟にモジュール化、そして再利用できるようになりました。

具体的には、サブプロジェクトとして利用されるアプリケーションには、それ独自の名前空間で、それ専用のルータを定義できます。

例えば、 conf/my.subproject.routes に次のようなルータを定義しておくとします。

GET   /                   my.subproject.controllers.Application.index

このルータは、メインとなるアプリケーションへ次のように組み込むことができます。

# トップページ
GET   /                   controllers.Application.index

# サブプロジェクトの組み込み
->    /my-subproject      my.subproject.Routes

# 静的コンテンツ
GET   /public/*file       controllers.Assets.at(file)

この設定によって、アプリケーション実行時に /my-subproject 以下の URL へ行われたアクセスは、全て my.subproject.controllers.Application.index というアクションの呼び出しに変換されます。

Note: メインとなるアプリケーション内の名前と衝突を起こさないように、サブプロジェクトとなるアプリケーションのコントローラは専用のサブパッケージ (上記の例では my.subproject がそうです) 以下に定義しましょう。同様に、サブプロジェクトとなるアプリケーションの Assets コントローラも同じ名前空間に定義したほうがよいでしょう。

この機能に関する詳細については サブプロジェクト関連のドキュメント を参照してください。

§Java API 向けの Http.Context プロパゲーション機能

Play 2.0 では、HTTP コンテキストが非同期コールバックの過程で失われてしまっていました。これは、非同期コールバックの過程で、コードを実行スレッドが、最初に HTTP リクエストを処理し始めたスレッドとは別のスレッドへの変わってしまうからです。

次の例を考えてみましょう。

public static Result index() {
  return async(
    aServiceSomewhere.getData().map(new Function<String,Result>(data) {
      // Ouch! You try to access the request data in an asynchronous callback
      String user = session().get("user"); 
      return ok("Here is the result " + user + ": " + data);
    })
  );
}

このコードは意図したとおりには動いていませんでした。その理由は、Play の非同期処理のアーキテクチャをよく知っていればわかるといえばわかるのですが、 Java 開発者にとってはあまり馴染みのない挙動でした。

この問題を解決する方法、つまり Http.Context を複数スレッドにまたがるスタックへ伝搬していく方法が発見されたため、このコードは Play 2.1 からは意図したとおりに動くようになりました。

§Java API 向けスレッドモデルの改善

排他制御を行わずに非同期コードをミュータブルなデータ構造に対して実行すると、何らかのレースコンディションに陥いる可能性が高いと思われます。Play が高度に非同期化されたノンブロッキングなコードを宣伝していることに加えて、 Java のデータ構造が大抵はミュータブルで非スレッドセーフであることを考えると、これまではアプリケーション側で排他制御の問題を解決する必要がありました。

次の例を考えてみましょう。

public static Result index() {

  final HashMap<String,Integer> result = new HashMap<String,Integer>();

  aService.doSomethingAsync().map(new Function<String,String>(key) {
    Integer i = result.get(key);
    if(i != null) {
      result.set(key, i++);
    }
    return key;
  });

  aService.doSomethingElse().map(new Function<String,String>(key) {
    result.remove(key);
    return null;
  });

  ...
}

上記のコードでは、二つのコールバックが result という HashMap を共有しています。したがって、これらコールバックが別々のスレッドで同時に実行された場合、レースコーンディションに陥る可能性が高いと思われます。そして、これが Java の HashMap 実装と相まると、結果的にアプリケーションが擬似的なデッドロック状態に陥ってしまうことが考えられます。

このような問題を回避するため、コールバックの実行に必要な排他制御がフレームワークレベルで行われるようになりました。 Play 2.1 では、同じ Http.Context に対してふたつ以上のコールバックが同時に実行されることはもうありません。同じコンテキスト内では、全てのコールバックは順番に(ただし、同じスレッドで実行されるという保証はありません)実行されます。

§コントローラクラスのインスタンス生成処理がカスタマイズ可能になった

初期状態では、 Play は URL をコントローラのメソッドに静的にバインドします。したがって、フレームワークによってコントローラのインスタンスが生成されることはなく、単に URL に応じた適切な static メソッドが呼び出されるだけです。しかし、場合によってはコントローラの生成処理をカスタマイズしたいこともあります。ルーティングの新しいシンタックスを使うとそれが可能です。

@ から始まるルート定義は Global::getControllerInstance メソッドによって処理されます。例えば、次のようなルートを定義します。

GET     /                  @controllers.Application.index()

この場合、 Play によって呼び出された getControllerInstance メソッドが controllers.Application のインスタンス(初期状態ではデフォルトコンストラクタによって生成されます)を返します。したがって、 DI フレームワークや独自のロジックによってコントローラクラスのインスタンス生成を行いたい場合、アプリケーションの Global クラスにおいて getControllerInstance メソッドをオーバーライドするとよいでしょう。

この デモアプリケーション のように、 Spring を始めとするあらゆる DI フレームワークを Play アプリケーションに組み込むことができます。

§Scala 向けの新しい JSON API

Scala 向けの新しい JSON API によって、JSON ツリーの変換やバリデーションなどの便利な機能が実現できました。詳細については Scala JSON コンビネータのドキュメント を参照してください。

§新たなフィルタ API と CSRF プロテクション機能

Play 2.1 では強力なフィルタ API が追加され、完全にノンブロッキングな方法で HTTP リクエストや HTTP レスポンスの部分をインターセプトできるようになりました。

この API を実現するため、既存の Action[A] という型の代替えとして、 EssentialAction という次のようなシンプルな型が追加されました。

RequestHeader => Iteratee[Array[Byte], Result]

これを利用すると、フィルタは次のように定義できます。

EssentialAction => EssentialAction

Note: 既存の Action[A] という型は互換性のために残されています。

Play のディストリビューションに含まれる filters プロジェクトには、標準的なフィルタがいくつか用意されています。例えば、 CSRF フィルタには CSRF 対策のために自動的にトークン管理を行う機能があります。

§RequireJS

Play 2.0 では、JavaScript は Google Closure の CommonJS モジュールサポートによって処理されていました。Play 2.1 からは、代わりに RequireJS が使われるようになりました。

実利用においてこれが意味するのは、特に何か設定しない限り、 Play は stage 、 dist 、 start 時のみ JavaScript を minify するということです。開発モード時は、クライアント側で JavaScript モジュール間の依存性が解決されます。

この機能を利用する場合は、ビルドファイル内の設定に RequireJS で管理したいモジュールを追加する必要があります。

requireJs := "main.js"

この機能に関する詳細については、 RequireJS 関連のドキュメント を参照してください。

§コンテンツネゴシエーション

コンテンツネゴシエーションのサポートが改善され、コントローラにおいてレスポンスに最適な言語が Accent-Language リクエストヘッダ値の quality 値に基いて自動的に選択されるようになりました。

加えて、同じリソースについて複数のレスポンス形式をサポートするような Web サービスを開発することが容易になりました。例えば、次のように記述すると、Accept リクエストヘッダ値に基いて適切なレスポンスの形式を選択することができます。

val list = Action { implicit request =>
  val items = Item.findAll
  render {
    case Accepts.Html() => Ok(views.html.list(items))
    case Accepts.Json() => Ok(Json.toJson(items))
  }
}

この機能についての詳細はコンテンツネゴシエーションに関するドキュメントの Scala 版 および Java 版 を参照してください。


このドキュメントの翻訳は Play チームによってメンテナンスされているものではありません。 間違いを見つけた場合、このページのソースコードを ここ で確認することができます。 ドキュメントガイドライン を読んで、お気軽にプルリクエストを送ってください。