h1. Play ライブラリ

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

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

* "Codec":/@api/play/libs/Codec.html: データをエンコードまたはデコードするユーティリティ
* "Crypto":/@api/play/libs/Crypto.html: 暗号化に関するユーティリティ
* "Expression":/@api/play/libs/Expression.html: 動的な式の評価
* "F":/@api/play/libs/F.html: Java による関数プログラミング
* "ファイル":/@api/play/libs/Files.html: ファイルシステム操作ヘルパ
* "I18N":/@api/play/libs/I18N.html: 国際化ヘルパ
* "IO":/@api/play/libs/IO.html: ストリーム操作ヘルパ
* "Images":/@api/play/libs/Images.html: イメージ操作ヘルパ
* "Mail":/@api/play/libs/Mail.html: e メール関数
* "MimeTypes":/@api/play/libs/MimeTypes.html: MIME タイプの取扱い
* "OAuth":/@api/play/libs/OAuth.html: OAuth クライアントプロトコル
* "OAuth2":/@api/play/libs/OAuth2.html: OAuth2 クライアントプロトコル
* "OpenID":/@api/play/libs/OpenID.html: OpenID クライアントプロトコル
* "Time":/@api/play/libs/Time.html: 時間と期間のユーティリティ
* "WS":/@api/play/libs/WS.html: 強力な Web サービスクライアント
* "XML":/@api/play/libs/XML.html: XML 構造のロード
* "XPath":/@api/play/libs/XPath.html: XPath による XML の解析

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

h2. XPath による XML の解析

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

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

bc. 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);
        …
    }
    
    …
}

h2. Web サービスクライアント

@play.libs.WS@ は、強力な HTTP クライアントを提供します。内部では "Async HTTP client":https://github.com/AsyncHttpClient/async-http-client を使用しています。

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

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

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

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

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

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

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

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

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

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

* @Option<T>@ (設定できる、または設定できない T 値)
* @Either<A,B>@ (どちらかが含まれる A 値または B 値)
* @Tuple<A,B>@ (どちらとも含まれる A 値と B 値)

h3. @Option<T>@, @Some<T>@ と @None<T>@

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

bc. /* 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);
}

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

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

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

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

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

h3. @Tuple<A, B>@

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

bc. 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();
}

そして:

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

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

h3. パターンマッチング

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

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

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

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

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

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

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

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

h3. Promises

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

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

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

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

h2. OAuth

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

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

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

* ユーザをプロバイダの認証ページへリダイレクトします
* ユーザが認証されると、プロバイダはユーザを未認証トークンと共にあなたのサーバへ再度リダイレクトします
* あなたのサーバは、サービスに対するリクエストを処理するために保存されなければならない、現在のユーザ向けに特化したアクセストークンと、この未認証トークンを交換します。この手順はサーバ間のコミュニケーションとして実行されます

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

h3. <a>OAuth 1.0</a>

OAuth 1.0 の機能は "oauth-signpost":http://code.google.com/p/oauth-signpost/ に基づく @play.libs.OAuth@ クラスによって提供されます。OAuth 1.0 は "Twitter":http://apiwiki.twitter.com/ や "Google":http://code.google.com/apis/accounts/docs/OAuth.html のようなサービスで使用されています。

サーバへ接続するためには、サービスプロバイダから得られる以下の情報を使ってOAuth.ServiceInfo インスタンスを作成する必要があります:
* リクエストトークン URL
* アクセストークン URL
* 認証 URL
* コンシューマ・キー
* コンシューマ・シークレット

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

bc. 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 を呼び出すことができるようになります:

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

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

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

h3. <a>OAuth 2.0</a>

OAuth 2.0 は、リクエストの署名を伴わないので、OAuth 1.0 よりもかなりシンプルです。OAuth 2.0 は "Facebook":http://developers.facebook.com/docs/authentication/ と "37signals":http://37signals.com で使用されています。

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

サーバへ接続するためには、サービスプロバイダから得られる以下の情報を使って OAuth2 インスタンスを作成する必要があります:
* アクセストークン URL
* 認証 URL
* クライアント ID
* シークレット

bc. 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);
}

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

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

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


h2. OpenID

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

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

* あらゆるリクエストにおいて、ユーザが接続されているか確認する
* もしユーザが接続されていなければ、ユーザが OpenID を投稿することのできるページを表示する
* ユーザを OpenID プロバイダにリダイレクトする
* ユーザが戻ってきたら、検証された OpenID を取得して HTTP セッションに保存する

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

bc. @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@ テンプレートです:

bc. #{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@ の定義は以下のようになります:

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

p(note). **考察を続けます**
 
それでは %(next)"非同期ジョブ":jobs% を使って、あらゆる HTTP リクエストの外側で操作を実行する方法を確認しましょう。