サンプルの実装

ManagedResourceStoreを使用するためには、まず「マニフェスト」と呼ばれるメタデータを記述する必要がある。このメタデータは以下に示すように、JSON形式で記述する。

manifest.json

{
  "betaManifestVersion": 1,
  "version": "1",
  "entries": [
      { "url": "canOffline.html"},
      { "url": "gears_init.js"}
    ]
}

「betaManifestVersion」は固定で1とするよう決められている。詳細は不明だが、恐らくマニフェストの形式を表すバージョン番号だろう。

「version」は非常に重要だ。値は任意の文字列になる。ManagedResourceStoreはオンライン状態ではマニフェストだけは必ずチェックし、「version」が以前と異なる値だった場合、キャッシュしているリソースを更新する必要があると判断する。逆に言うと、Gearsにキャッシュさせたリソースを更新した場合は、「version」を更新しないとキャッシュに反映されないということだ。

「entries」は、ManagedResourceStoreにキャッシュすべきリソースのURLを複数指定する。「http://~」で始まる絶対URLか、マニフェストファイルの位置からの相対URLを指定する。ここでは、以降で示す二つのファイル(HTMLとJavaScript)を指定している。

次は、Gearsを使用するための定型的なJavaScriptコードだ。このコードをコピーして、gears_init.jsというファイル名で保存する。このファイルはGoogleが著作権を保持しており、Gearsを使用する上で必ず必要となる定型的なファイルなので、詳細な説明は省略する(詳しく知りたければ、まずコメントを読んでいただきたい)。

gears_init.js

// Copyright 2007 Google Inc. All Rights Reserved.
//
// Sets up google.gears.*, which is *the only* supported way to access Gears.
//
// Circumvent this file at your own risk!
//
// In the future, Gears may automatically define google.gears.* without this
// file. Gears may use these objects to transparently fix bugs and compatibility
// issues. Applications that use the code below will continue to work seamlessly
// when that happens.

(function() {
  // We are already defined. Hooray!
  if (window.google && google.gears) {
    return;
  }

  var factory = null;

  // Firefox
  if (typeof GearsFactory != 'undefined') {
    factory = new GearsFactory();
  } else {
    // IE
    try {
      factory = new ActiveXObject('Gears.Factory');
    } catch (e) {
      // Safari
      if (navigator.mimeTypes["application/x-googlegears"]) {
        factory = document.createElement("object");
        factory.style.display = "none";
        factory.width = 0;
        factory.height = 0;
        factory.type = "application/x-googlegears";
        document.documentElement.appendChild(factory);
      }
    }
  }

  // *Do not* define any objects if Gears is not installed. This mimics the
  // behavior of Gears defining the objects in the future.
  if (!factory) {
    return;
  }

  // Now set up the objects, being careful not to overwrite anything.
  if (!window.google) {
    window.google = {};
  }

  if (!google.gears) {
    google.gears = {factory: factory};
  }
})();

最後に紹介するのが、このサンプルの中核を成すHTML + JavaScriptコードだ。重要なポイントには番号を振ってあり、後で詳しく解説している。

canOffline.html

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS" />

    <!-- gears_init.jsの読み込み -->
    <script type="text/javascript" src="gears_init.js"></script>
    <script type="text/javascript"><!--

        // (1) 使用するリソースストアにつける名称
        var STORE_NAME = "sample_offline_store";

        // (2) ローカルサーバと、リソースストアの作成
        var localServer = google.gears.factory.create("beta.localserver","1.0");
        var store = localServer.createManagedStore(STORE_NAME);

        // マニフェストファイルに書かれているエントリをキャッシュに保存する関数
        function capture() {

            // (3) マニフェストファイルのURLを指定し、リソースのチェック
            store.manifestUrl = "manifest.json";
            store.checkForUpdate();

            // (4) 更新結果を0.5秒間隔でチェック
            var timerId = window.setInterval(function() {

                // currentVersionプロパティが値を持っていた場合、マニフェストファイルに
                // 列挙されたエントリのキャプチャが全て完了したということ。
                if (store.currentVersion) {
                    // 更新は成功。0.5秒おきのチェックを取りやめる。
                    window.clearInterval(timerId);
                    alert("キャッシュへの保存が成功しました!");
                } else if (store.updateStatus == 3) {
                alert("エラーが発生しました: " + store.lastErrorMessage);
                }
            }, 500);
        }

        function clear() {
            (5) リソースストアの削除
            localServer.removeManagedStore(STORE_NAME);
            alert("キャッシュをクリアしました。");
        }
        //--></script>
</head>
<body>
    <h2>Google Gears ローカルサーバのテスト</h2>
    <button onclick="capture()">キャッシュに保存</button>
    <button onclick="clear()">キャッシュを削除</button>
</body>
</html>

(1) リソースストアは、名前を付けて管理する。その名前を指定してリソースストアの作成や削除を行うことが、そのままキャッシュの作成/削除につながるわけだ。

(2) ここでは、GearsのAPIを使用してローカルサーバの生成とリソースストアの生成を行っている。ローカルサーバと言っても、ネットワークソケットを開いてクライアントからの接続を待つ一般的な意味でのサーバではなく、「キャッシュ」とほとんど同義だ。リソースストアの生成には、(1)で定義した文字列を使用している。

(3) リソースストアの属性「manifestUrl」に、マニフェストファイルのURLを指定し、メソッド「checkForUpdate」を呼び出すことで、キャッシュを更新すべきかどうかチェックしている。checkForUpdateは、まずマニフェストファイルのversionが更新されているかどうかチェックし、更新されていた場合はマニフェストで指定されたリソースを最新のものに更新する。

(4) checkForUpdateメソッドは非同期で実行されるため、キャッシュの更新に成功したかどうかを0.5秒間隔でポーリングし、チェックしている。チェック方法は、リソースストアのcurrentVersionプロパティがnull以外の値になったら成功、それ以外はupdateStatusプロパティのチェック(値が0の場合は更新が完了している、1の場合はマニフェストのチェック中、2の場合はリソースのダウンロード中、3の場合は更新失敗)を行うというものだ。エラーが発生した場合は、リソースストアのlastErrorMessageプロパティにエラーメッセージが格納される(このチェックはポーリングで行うのではなく、状態が変化したときにコールバック関数を呼び出してもらうようなAPIの方が良いと思うのだが、そうしない理由があるのだろうか)。

(5) GearsのAPIを使用して、リソースストアを削除している。リソースストアの名称は、作成した時と同じものを指定する。

これらのファイルを全て同じディレクトリに保存し、HTTPサーバで公開すれば、先ほどお見せしたようなページが表示されるはずだ。そこで「キャッシュに保存する」ボタンを押した後は、サーバを止め、ブラウザのキャッシュを削除したとしてもページが表示され続ける。

もしうまくいったら、今度はファイルを編集してみてほしい。たとえリソースのタイムスタンプを更新したとしても、マニフェストのversionを変更しない限り、Gearsはリソースが更新されたとはみなさないということを実感できるはずだ。

最後に

駆け足ではあるが、Gearsの雰囲気は伝えられたであろう。オフライン技術は、現在Web技術の分野でかなりホットなトピックであり、やはりGoogleも自分たちなりの答えを用意していたようである。

また執筆する機会があれば、次はGearsが持つデータベース機能の解説を行いたいと思う。

そして、今回は紹介できなかったが、Gearsには必ずしもオフラインの用途だけではないAPIも存在する。よく考えると、ブラウザのプラグインとしてJavaScript APIを提供するというGearsのコンセプトは、いくらでも便利なJavaScript APIをブラウザに提供できるということに繋がっている。クライアントマシンへのGearsの配布が順調に進めば、Ajax開発が今より格段に楽になるだろう。

非常に楽しみな技術がまた一つ増えた。