2010-04-26

AppEngine Java と JSONIC と OpenSocial OAuth Filter で作る OpenSocial アプリの外部サーバ

おはようございます。なかじまんです。

AppEngine Java と JSONIC と OpenSocial OAuth Filter を使うと、OpenSocial アプリの外部サーバをお手軽に立てることができますよ ... というおはなしです。ただ、大規模な運用実績はありませんので、その点は割り引いて読んでくださいね。

はじめに、JSONIC というライブラリを使って、AppEngine Java 上に、独自の REST API を構築します。そして、OpenSocial アプリから gadgets.io.makeRequest メソッドを使って、その REST API を呼び出すことにします。JSONIC とは、↓というものです。

JSONIC - simple json encoder/decoder for java
JSONICは、Java用のシンプルかつ高機能なJSONエンコーダー/デコーダーライブラリです。Java用のJSONライブラリはすでに多数存在しますが、JSONICはRFC 4627に従った正式なJSON形式でのデコード/エンコードを行いながらも、プログラミング言語に依存する情報をJSON内に含めることなくPOJO(Plain Old Java Object)と自然な変換を行える点に特徴があります。
次のとおり、REST API のインタフェースを想定します。

* GET http://example.com/api/entries.json?count={count} // エントリ取得
* GET http://example.com/api/entries/{id}.json // エントリ取得
* POST http://example.com/api/entries.json // エントリ投稿
* PUT http://example.com/api/entries/{id}.json // エントリ更新
* DELETE http://example.com/api/entries/{id}.json // エントリ削除

jsonic-1.2.0.jar をビルドパスに追加して、次のとおり、web.xml に JSONIC の REST サーブレットを追記します。このとき、config パラメータの mappings を使って、REST API のインタフェースとクラスの対応を指定します。ここでは、EntryService クラスとします。
<servlet>
  <servlet-name>JSONIC</servlet-name>
  <servlet-class>net.arnx.jsonic.web.RESTServlet</servlet-class>
  <init-param>
    <param-name>config</param-name>
    <param-value>
    {
      "mappings": {
        "/api/entries.json": "com.example.EntryService",
        "/api/entries/{id}.json": "com.example.EntryService"
      }
    }
    </param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>JSONIC</servlet-name>
  <url-pattern>*.json</url-pattern>
</servlet-mapping>
次のとおり、EntryService クラスを記述します。find が GET リクエスト、create が POST、update が PUT、delete が DELETE を表します。リクエストのパラメータは、各メソッドの params に格納されます。また、各メソッドの戻り値が JSON 形式のレスポンスに相当します。
public class EntryService {
  public Map<String,Object> find(Map<String,String> params) {
    Map<String, Object> json = new HashMap<String, Object>();
    json.put("viewer", params.get("opensocial_viewer_id"));
    json.put("id", params.get("id"));
    json.put("count", params.get("count"));
    return json;
  }
  public Map<String,Object> create(Map<String,String> params) {
    ...
  }
  public Map<String,Object> update(Map<String,String> params) {
    ...
  }
  public Map<String,Object> delete(Map<String,String> params) {
    ...
  }
}
次のとおり、OpenSocial アプリから gadgets.io.makeRequest メソッドを使って、その REST API を呼び出します。
var url = 'http://example.com/api/entries/1.json?count=10';
var params = {};
params[gadgets.io.RequestParameters.METHOD] =
  gadgets.io.MethodType.GET;
params[gadgets.io.RequestParameters.AUTHORIZATION] =
  gadgets.io.AuthorizationType.SIGNED;
params[gadgets.io.RequestParameters.CONTENT_TYPE] =
  gadgets.io.ContentType.JSON;
gadgets.io.makeRequest(url, function(res) {
  console.log(res.data.viewer);
  console.log(res.data.id);
  console.log(res.data.count);
}, params);
PUT リクエストと DELETE リクエストが使えないコンテナのときは、リクエストのパラメータに _method=put や _method=delete を指定すると、疑似的に PUT や DELETE リクエストを表現できます。

次に、OpenSocial OAuth Filter というライブラリを使って、構築した REST API に対するリクエストの署名を検証するようにします。 OpenSocial OAuth Filter とは、↓というものです。

OpenSocial OAuth Filter
OpenSocial OAuth Filter is a JavaEE filter to validate OpenSocial requests. This filter blocks illegal requests to prevent spoofing and tampering.
opensocial-oauth-filter-0.7.jar をビルドパスに追加して、次のとおり、web.xml に OpenSocial OAuth Filter のフィルタサーブレットを追記します。このとき、config-class パラメータを使って、公開鍵と検証条件を実装したクラスを指定します。ここでは、MixiRegistryConfigurator クラスとします。
<filter>
  <filter-name>opensocial-oauth-filter</filter-name>
  <filter-class>
    org.hidetake.opensocial.filter.RequestValidationFilter
  </filter-class>
  <init-param>
    <param-name>config-class</param-name>
    <param-value>com.example.MixiRegistryConfigurator</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>opensocial-oauth-filter</filter-name>
  <url-pattern>*.json</url-pattern>
</filter-mapping>
次のとおり、MixiRegistryConfigurator クラスを記述します。公開鍵とコンシューマキー、アプリID、ガジェットURL を指定します。この条件を満たさないリクエストには、403 Forbidden をレスポンスします。
public class MixiRegistryConfigurator
    implements RegistryConfigurator {
  private static final String cert =
    "-----BEGIN CERTIFICATE-----\n"
    + "MIICdzCCAeCgAwIBAgIJANCWpLIspxwbMA0GCSqGSIb3DQEBBQUAMDIxCzAJBgNV\n"
      ...
    + "IpqnsHwF1pm0bTY=\n"
    + "-----END CERTIFICATE-----";
  @Override
  public void configure(AppRegistry registry)
      throws ConfigurationException {
    registry.register(new OpenSocialApp(
      "16271", "http://example.com/gadgets.xml",
      OpenSocialApp.createOAuthAccessorRSASHA1("mixi.jp", cert)));
  }
  @Override
  public void configure(ExtensionRegistry registry)
      throws ConfigurationException {
    registry.register(new ValidationLogger());
  }
}
最後に、EntryService クラスで、AppEngine のデータストレージを使って、エントリを検索したり、保存したり、削除するように実装すればよいでしょう。

-- 2010-04-28 追記

CREYLE 用 RegistryConfigurator クラスの雛形を置いておきますので、参考にしてください。

CrelyeRegistryConfigurator.java

2010-04-05

OpenSocial jQuery Network Distribution 配布URL変更のお知らせ

こんばんは。なかじまんです。

OpenSocial jQuery Network Distribution の配布先URLを変更します。

4/9(金)までに scripts.lrlab.to から d2hv4ldeur9lfv.cloudfront.net へホスト名
の変更をお願いします。

変更前 -- 4/9(金) まで参照できます

* http://scripts.lrlab.to/opensocial-jquery-1.3.2.5.min.js
* http://scripts.lrlab.to/opensocial-jquery-1.2.6.5.min.js
* http://scripts.lrlab.to/opensocial-jquery.templates-0.1.0.min.js
* http://scripts.lrlab.to/opensocial-jquery.autoHeight-1.0.0.min.js
* http://scripts.lrlab.to/opensocial-jquery.minimessage-1.0.0.min.js
* http://scripts.lrlab.to/opensocial-jquery.mixi-1.0.0.min.js

変更後 -- 今すぐ参照できます

* http://d2hv4ldeur9lfv.cloudfront.net/opensocial-jquery-1.3.2.5.min.js
* http://d2hv4ldeur9lfv.cloudfront.net/opensocial-jquery-1.2.6.5.min.js
* http://d2hv4ldeur9lfv.cloudfront.net/opensocial-jquery.templates-0.1.0.min.js
* http://d2hv4ldeur9lfv.cloudfront.net/opensocial-jquery.autoHeight-1.0.0.min.js
* http://d2hv4ldeur9lfv.cloudfront.net/opensocial-jquery.minimessage-1.0.0.min.js
* http://d2hv4ldeur9lfv.cloudfront.net/opensocial-jquery.mixi-1.0.0.min.js

※scripts.lrlab.to は d2hv4ldeur9lfv.cloudfront.net の CNAME ですので、ファイルの実体は同じものです。

背景としては、ここ半年でアクセス数が伸びてきており、独自ドメインで参照させていると、何かトラブルがあったとき、みなさんへの影響が大きいという判断からです。少なくとも、Amazon のホスト名で、名前を引いた方がより安全(調べてませんがレスポンスもよさそう)であろうということです。

お手数をお掛けしますが、ご協力のほどよろしくお願いします。