Documentation

You are viewing the documentation for Play 1. The documentation for Play 2 is here.

Play ライブラリ

play.libs パッケージには、一般的なプログラミングタスクを達成する手助けとなる便利ないくつかのライブラリが含まれています。

これらライブラリのほとんどは本当に直感的に使うことのできるシンプルなヘルパです:

以下の節では、特に重要なライブラリについて、より詳細な情報を提供します。

XPath による XML の解析

XPath は、コード生成ツールを使わずに XML ドキュメントを解析する、おそらくもっとも簡単な方法です。 play.libs.XPath ライブラリは、このタスクを効率的に達成するために必要な基本機能を提供します。

XPath 演算子はすべての org.w3.dom.Node 型を操作します:

org.w3.dom.Document xmlDoc = … // retrieve a Document somewhere
  
for(Node event: XPath.selectNodes("events//event", xmlDoc)) {
    
    String name = XPath.selectText("name", event);
    String data = XPath.selectText("@date", event);
    
    for(Node place: XPath.selectNodes("//place", event)) {
        String place = XPath.selectText("@city", place);
        …
    }
    
    …
}

Web サービスクライアント

play.libs.WS は、強力な HTTP クライアントを提供します。内部では Async HTTP client を使用しています。

リクエストの作成は簡単です:

HttpResponse res = WS.url("http://www.google.com").get();

いったん HttpResponse オブジェクトを手に入れてしまえば、すべてのレスポンス属性にアクセスすることができます。

int status = res.getStatus();
String type = res.getContentType();

複数の content-type においてボディの内容を検索することもできます:

String content = res.getString();
Document xml = res.getXml();
JsonElement json = res.getJson();
InputStream is = res.getStream();

async API を使ってノンブロッキングな HTTP リクエストを作成することもできます。この場合は Promise<HttpResponse> を受け取ります。いったん非同期状態から回復すれば、通常通り HttpResponse を使うことができます:

Promise<HttpResponse> futureResponse = WS.url(
    "http://www.google.com"
).getAsync();

Java による関数プログラミング

play.libs.F ライブラリは関数プログラミング由来の便利な構成物を提供します。これらの構成物は複雑に抽象化された事象を取り扱うために使用されます。関数プログラミングに慣れている人のために、以下が提供されています:

Option<T>, Some<T>None<T>

いくつかのケースでは結果を返さないかもしれない関数 (例えば find) を記述する場合において、一般的な(悪い)Java のパターンは結果がない場合に null を返すことです。この方法は、関数の戻り値の型がオブジェクトを返さない可能性があることを明確に示していないし、null 参照の発明者によって “billion-dollar mistake” として認識されているおり、危険です。
Option<T> はこの問題に対するエレガントなソリューションです: T 型のオブジェクトを返す代わりに、この関数は Option<T> を返します。この関数は成功した場合、(実際の結果をラップする) Some<T> 型、そうでない場合は None<T> 型、いずれも Option<T> のサブタイプであるオブジェクトを返します。
以下に例を示します:

/* Safe division (will never throw a runtime ArithmeticException) */
public Option<Double> div(double a, double b) {
    if (b == 0)
        return None();
    else
        return Some(a / b);
}

この関数のこのように使います:

Option<Double> q = div(42, 5);
if (q.isDefined()) {
    Logger.info("q = %s", q.get()); // "q = 8.4"
}

しかし、これを使うために Option<T>Iterable<T> を実装していることを利用したもっと便利な構文があります:

for (double q : div(42, 5)) {
    Logger.info("q = %s", q); // "q = 8.4"
}

この for ループの中身は div 関数が成功した場合にのみ、一回だけ実行されます。

Tuple<A, B>

便利な Tuple<A, B> は、 A および B 型の二つのオブジェクトをラップします。これらのオブジェクトは、それぞれ _1 および _2 フィールドを使って取得することができます。例えば:

public Option<Tuple<String, String>> parseEmail(String email) {
    final Matcher matcher = Pattern.compile("(\\w+)@(\\w+)").matcher(email);
    if (matcher.matches()) {
        return Some(Tuple(matcher.group(1), matcher.group(2)));
    }
    return None();
}

そして:

for (Tuple<String, String> email : parseEmail("[email protected]")) {
    Logger.info("name = %s", email._1); // "name = foo"
    Logger.info("server = %s", email._2); // "server = bar.com"
}

T2<A, B> クラスは Tuple<A, B> のエイリアスです。三要素のタプルを扱う場合は T3<A, B, C> クラスを使い、 T5<A, B, C, D, E> まで同様です。

パターンマッチング

Java にはパターンマッチングが必要だと感じることがありました。残念ながら Java は組み込みのパターンマッチングを持ち合わせておらず、関数構造の欠如により、パターンマッチングをライブラリとして追加することも困難でした。とにかく、私たちはそれほど悪くない解決策に取り組みました。

私たちのアイディアとは、Java において基礎的なパターンマッチングを達成するために最新の‘for ループ’文法を使うことでした。パターンマッチングは、オブジェクトが求められる条件に合致することを確認し、関心のある値を抽出しなければなりません。Play のパターンマッチングライブラリは play.libs.F ライブラリの一部です。

簡単な例を見てみましょう; Object 型の参照があり、これが “command:” から始まる文字列であることを確認したいとします。

標準的な方法は以下のようになるでしょう:

Object o = anything();
 
if(o instanceof String && ((String)o).startsWith("command:")) {
    String s = (String)o;
    System.out.println(s.toUpperCase());
}

Play のパターンマッチングを使うと、これを次のように書くことができます:

for(String s: String.and(StartsWith("command:")).match(o)) {
    System.out.println(s.toUpperCase());
}

この for ループは条件に合致する場合に一度だけ実行され、キャストをする必要なく自動的に String の値を抽出します。明示的なキャストが存在しないので、すべてがタイプセーフであり、コンパイラによって型チェックが行われます。

Promises

Promise は Play による Future 型のカスタマイズです。実際のところ、 Promise<T>Future<T> でもあるので、標準的な Future として使用することもできます。しかし、 Promise にはとても興味深い属性: onRedeem(…) を使って、期待する値が利用可能になるとできるだけ早く呼び出されるコールバックを登録する機能が備わっています。

Promise インスタンスは Play のいたるところ (Jobs や WS.async, その他…) で Future の代わりに使用されています。

Promise はいくつかの方法で紐付けることができます。例えば:

Promise p = Promise.waitAll(p1, p2, p3)
Promise p = Promise.waitAny(p1, p2, p3)
Promise p = Promise.waitEither(p1, p2, p3)

OAuth

OAuth は、デスクトップアプリケーションや web アプリケーションに向けた、シンプルで標準的なアプローチを使ったセキュアな認証 API 用オープンプロトコルです。

二つの異なる仕様が存在します: OAuth 1.0 と OAuth 2.0 です。Play は、これらのうちどちらかの仕様を提案するサービスにコンシューマとして接続するライブラリを提供します。

一般的な手順は次のようになります:

Play フレームワークは、この手順の大部分を取り計らいます。

OAuth 1.0

OAuth 1.0 の機能は oauth-signpost に基づく play.libs.OAuth クラスによって提供されます。OAuth 1.0 は TwitterGoogle のようなサービスで使用されています。

サーバへ接続するためには、サービスプロバイダから得られる以下の情報を使ってOAuth.ServiceInfo インスタンスを作成する必要があります:

アクセストークンはこのようにして読み出すことができます:

public static void authenticate() {
    // TWITTER is a OAuth.ServiceInfo object
    // getUser() is a method returning the current user 
    if (OAuth.isVerifierResponse()) {
        // We got the verifier; 
        // now get the access tokens using the request tokens
        OAuth.Response resp = OAuth.service(TWITTER).retrieveAccessToken(
            getUser().token, getUser().secret
        );
        // let's store them and go back to index
        getUser().token = resp.token; getUser().secret = resp.secret;
        getUser().save()
        index();
    }
    OAuth twitt = OAuth.service(TWITTER);
    Response resp = twitt.retrieveRequestToken();
    // We received the unauthorized tokens 
    // we need to store them before continuing
    getUser().token = resp.token; getUser().secret = resp.secret;
    getUser().save()
    // Redirect the user to the authorization page
    redirect(twitt.redirectUrl(resp.token));
}

これで、トークンペアを使って署名したリクエストによって API を呼び出すことができるようになります:

mentions = WS.url(url).oauth(TWITTER, getUser().getTokenPair()).get().getString();

この例ではエラーをチェックしていませんが、本番環境ではチェックすべきです。 OAuth.Response オブジェクトは、エラーが起こった場合には null でないエラーフィールドを保持します。それらの多くは、ユーザがあなたにアクセス権を与えていないか、プロバイダがダウンしているか、あるいはプロバイダにバグがある場合です。

完全な使用例が samples-and-tests/twitter-oauth にあります。

OAuth 2.0

OAuth 2.0 は、リクエストの署名を伴わないので、OAuth 1.0 よりもかなりシンプルです。OAuth 2.0 は Facebook37signals で使用されています。

OAuth 2.0 の機能は play.libs.OAuth2 として提供されています。

サーバへ接続するためには、サービスプロバイダから得られる以下の情報を使って OAuth2 インスタンスを作成する必要があります:

public static void auth() {
    // FACEBOOK is a OAuth2 object
    if (OAuth2.isCodeResponse()) {
        // authUrl must be the same as the retrieveVerificationCode call
        OAuth2.Response response = FACEBOOK.retrieveAccessToken(authUrl);
        // null if an error occurred
        String accessToken = response.accessToken;
        // null if the call was a success
        OAuth2.Error = response.error;
        // Save accessToken, you will need it to request the service
        index();
    }
    // authUrl is a String containing an absolute URL where the service 
    // should redirect the user back
    // This will trigger a redirect
    FACEBOOK.requestVerificationCode(authUrl);
}

一旦このアクセストークンをユーザと関連づけたあとは、ユーザに代わってサービスへ問い合わせを行うためにこのトークンを使うことができます:

WS.url(
    "https://graph.facebook.com/me?access_token=%s", access_token
).get().getJson();

完全な使用例が samples-and-tests/facebook-oauth2 にあります。

OpenID

OpenID オープンで分散した識別システムです。アプリケーションに特化したユーザ情報を保持することなしに、容易に新しいユーザを受け入れることができます。ユーザの OpenID を通して認証されたユーザを追跡し続けなければならないだけです。

この例では、Play アプリケーションにおいて OpenID 認証がどのように使用されるかを高いレベルで見ることができます:

OpenID の機能は play.libs.OpenID クラスとして提供されています。

@Before(unless={"login", "authenticate"})
static void checkAuthenticated() {
    if(!session.contains("user")) {
        login();
    }
}
 
public static void index() {
    render("Hello %s!", session.get("user"));
}
     
public static void login() {
    render();
}
    
public static void authenticate(String user) {
    if(OpenID.isAuthenticationResponse()) {
        UserInfo verifiedUser = OpenID.getVerifiedID();
        if(verifiedUser == null) {
            flash.error("Oops. Authentication has failed");
            login();
        } 
        session.put("user", verifiedUser.id);
        index();
    } else {
        if(!OpenID.id(user).verify()) { // will redirect the user
            flash.error("Cannot verify your OpenID");
            login();
        } 
    }
}

そして、以下が login.html テンプレートです:

#{if flash.error}
<h1>${flash.error}</h1>
#{/if}
 
<form action="@{Application.authenticate()}" method="POST">
    <label for="user">What’s your OpenID?</label>
    <input type="text" name="user" id="user" />
    <input type="submit" value="login..." />
</form>
</code>

最後に、 routes の定義は以下のようになります:

GET   /                     Application.index
GET   /login                Application.login
*     /authenticate         Application.authenticate

考察を続けます

それでは 非同期ジョブ を使って、あらゆる HTTP リクエストの外側で操作を実行する方法を確認しましょう。