Google App Engine 1.3.6 のNamespace API
先日プレリリースされていたApp Engine 1.3.6が正式にリリースされた。今回の目玉は
小ネタとしては
- Java でもapp.yamlが使えるように
- 管理コンソールからタスクキューが停止できるように
- 管理コンソールのダッシュボードのグラフが30日分表示されるようになった
- ブロブストアでContent-Rangeヘッダが利用可能に
とりあえず、今回はNamespaceによるマルチテナント化についてだけ。
NamespaceManager API
一つのアプリケーションを、異なるデータセットに対して動作させることを簡単にできるようにするAPIです。つまり名前空間Aと名前空間Bに対して異なるデータセットを用意し、それぞれ独立に動作させることができます。
MemcacheService ms = MemcacheServiceFactory.getMemcacheService(); NamespaceManager.set("A"); ms.put("key", "value1"); // 名前空間Aで書き込み NamespaceManager.set("B"); ms.put("key", "value2"); // 名前空間Bで書き込み
名前空間名としては、数字、アルファベット、ピリオド、アンダースコア、ハイフンから構成される100文字までの文字列を利用することができます。それ以外の文字は利用できません。また、アンダースコアから始まる文字列は一応システムリザーブということになっているので、ユーザは使ってはいけません。ただしこのルールは単なるコンベンションなので使ってしまうことはできるようですが、避けた方がよいでしょう。
目的はデータセットの切り分けなので、名前空間が対応しているAPIはMemcacheとデータストア、あとはタスクキューです。データ系APIとしてはブロブストアAPIもあるのですが、ブロブストアは名前空間に対応していません。ブロブストアではキーをプログラマが指定できないから問題ないということなのでしょうが、本当に重要なデータを名前空間で切り分けたい場合には、アクセスする際に明示的に制御する必要があります。この方法としては、ブロブキーをアプリケーションの外に出さず、常にデータストア経由でブロブキーをハンドルする方法が推奨されています。
複数のGoogle Apps ドメインへの対応
名前空間の一番の使い道は、複数の Google Appsドメインに対応することだと思われます。App Engine のアプリケーションは、複数のGoogle Apps ドメイン名でアクセスできるように設定できますが、普通にやるとそれぞれのドメインに対するデータが混ざり合ってしまいます。アプリケーション自体を複数作成して、ドメインに割り当ててやればいいのですが、それはそれで面倒です。
名前空間を使うとこれがすっきり書けます。NamespaceManagerには、getGoogleAppsNamespaceというメソッドがあり、これを利用してやるのです。さらに、サーブレット標準のフィルタ機構を用いて、フィルタ内で名前空間の設定を行うようにしてやれば、アプリケーション内部は一切変更せずに、名前空間を利用するようにすることができます。
以下のコードはドキュメントから持ってきたものです。まずフィルタを定義します。
public class NamespaceFilter implements javax.servlet.Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { // 名前空間が設定されていない場合のみ、Appsの名前空間を設定 if (NamespaceManager.get() == null) { NamespaceManager.set(NamespaceManager.getGoogleAppsNamespace()); } } .... }
あとはweb.xml ですべてのURLに対してこのフィルタが動作するように設定します。
<filter> <filter-name>NamespaceFilter</filter-name> <filter-class>(put the class path here).NamespaceFilter</filter-class> </filter> <filter-mapping> <filter-name>NamespaceFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>