最速!Google Wave API解説

第1回ガジェットAPI

2009年5月28日、GoogleのデベロッパーカンファレンスGoogle I/O 2009のキーノートにおいて大々的に発表されたGoogle Waveは、聴衆に熱狂を持って迎えられました。そのキーノートにおいてスピーカーは「Google WaveはProductであり、Platformであり、Protocolだ」と述べました編注⁠。

本連載ではその3つのPの内の2つ目、PlatformとしてのGoogle Waveについて説明します。

編注
著者執筆のGoogle I/O 2009のイベントレポート記事も参照ください。

なお、Google Waveは現在も急ピッチで開発が進められているプロダクトであり、本連載に掲載する画面遷移・ソースコードなどは最新ではない可能性があります。記事に沿って作業していて違和感を覚えた場合にはGoogleの公開しているドキュメントを適宜参照してください。

API概観

私たち開発者から見えるPlatformとしてのGoogle Waveは3種類のAPIからなります。

Robots API Extensions Waveを拡張するためのAPI
Gadgets API
Wave Embed API Waveの機能を外部から利用するためのAPI

APIごとの違いを大まかに説明すると以下のようになります。

Robots API
Waveでの入力をサーバ側で処理をするためのAPI。例:チャットボット(人工無能)
Gadgets API
クライアント側でWave内のイベントを処理をするためのAPI。JavaScriptで記述することが多い。例:Google Mapガジェット
Wave Embed API
Google Wave以外のサービスにGoogle Waveの機能を組み込むためのAPI

3つのうちRobots APIとGadgets APIはGoogle Wave自体に機能を追加するAPIとしてExtensionsと呼ばれ、Wave Embed APIとは区別されています。

連載第1回目の今回は、上記3つのうちExtensionsに所属するGadgets APIについて説明します。公式ドキュメントではRobots APIが先に説明されていますが、Gadgets APIはGoogle Waveの大きな特徴の一つであるリアルタイム通信を扱うAPIであることと、Robots APIがGadgets APIを操作することもできるAPIであることから、Gadgets APIを先に説明した方がわかりやすいでしょう。

Gadgets API

シンプルなガジェット

ガジェットとはHTML、CSS、JavaScriptなどをXMLで包みiGoogleやOpenSocial等のコンテナに機能を追加するための仕組みで、Google Waveのガジェットも基本的にはiGoogleやOpenSocial等と全く同じです。これは実際にソースコードを見た方が早いでしょう。公式ドキュメントから引用してみます。

リスト1 シンプルなガジェット公式ドキュメントより引用
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Hello Wave">
    <Require feature="rpc" />
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
    <script type="text/javascript"
       src="http://wave-api.appspot.com/public/wave.js"></script>
       Hello, Wave!
    ]]>
  </Content>
</Module>

<Require feature="rpc" /> タグでRPCコールの使用を宣言している部分と、<Content>タグ内部でwave.jsスクリプトを読み込んでいる部分が特徴的ですが、このシンプルな例ではiGoogleなどのガジェットと何ら変わるところがないことが理解できるでしょう。

ガジェットの実行

それではガジェットを実際にWave上で実行してみましょう。ガジェットのXMLにはグローバルなURIが必要ですが、本XMLはすでにhttp://gadget-doc-examples.googlecode.com/svn/trunk/wave/hello.xmlに用意されていますので、このURIをそのまま使います。

まずは真ん中のWaveにある「New Wave」ボタンを押下して新規にWaveを開きます。

図1 New Waveボタン
図1 New Waveボタン

画面右上の、ユーザー名の隣にある「Debug」ボタンを押下します。

図2 Debugボタン
図2 Debugボタン

メニューから「Add Gadget」を選択すると「Gadget gallery」が開きます。

図3 Debugメニュー
図3 Debugメニュー
図4 Gadget galleryダイアログ
図4 Gadget galleryダイアログ

Gadget galleryの一番下にある「URL of gadget module XML」テキストフィールドにガジェットXMLのURIを入力し、⁠Add by XML」ボタンを押下すると、先ほど作成したWaveに指定したガジェットが挿入されます。

図5 挿入されたガジェット
図5 挿入されたガジェット

挿入されたガジェットに先ほどXMLで指定した Hello, Wave! が表示されていることを確認してください。

Shared Stateを使用したガジェット

ここまでで非常にシンプルながらWave上に自作のガジェットを表示することに成功しました。しかしこのままでは何かが足りないと思いませんか?

そうです。まだこのガジェットにはWave参加者のリアルタイムなやり取りが実装されていません。参加者同士でリアルタイムにデータをやり取りするにはShare Stateと呼ばれるKey-Value型のオブジェクトを使用します。これもまずは公式ドキュメントの例を見てみましょう。

リスト2 Shared Stateを使用したガジェット公式ドキュメントより引用
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="State Example" height="120">
  <Require feature="rpc" />
</ModulePrefs>
<Content type="html">
<![CDATA[
  <div id="content_div" style="height: 50px;"></div><!-- カウンタを表示 -->
  <script type="text/javascript"
      src="http://wave-api.appspot.com/public/wave.js"></script>
  <script type="text/javascript">

    var div = document.getElementById('content_div');

    // countの値をインクリメントする関数
    function buttonClicked() {
      // countの値を取得して数値に変換。なかった場合は0になる
      var value = parseInt(wave.getState().get('count', '0'));

      wave.getState().submitDelta({'count': value + 1});
    }

    // countの中身を表示する関数
    function stateUpdated() {
      // 共有ステートにcountがあるかないかを確認
      if(!wave.getState().get('count')) {
        // なければ0と表示
        div.innerHTML = "The count is 0."
      }
      else {
        // あればcountの値を取得して表示
        div.innerHTML = "The count is " + wave.getState().get('count');
      }
    }

    function main() {
      if (wave && wave.isInWaveContainer()) {
        // Shared Stateが変更されたときに実行される関数を登録
        wave.setStateCallback(stateUpdated);
      }
    }

    // ガジェットがロードされたときに実行される関数を登録
    gadgets.util.registerOnLoadHandler(main);

    // countの値を0にリセットする関数
    function resetCounter(){
      wave.getState().submitDelta({'count': '0'});
    }

  </script>
  <input type=button value="Click Me!" id="butCount" onClick="buttonClicked()">
  <input type=button value="Reset" id="butReset" onClick="resetCounter()">
]]>
</Content>
</Module>

上記のソースで赤くなっている部分がそのShared Stateを扱っている箇所です。3種類の関数しか使われていないので抜き出してみます。

wave.setStateCallback(FUNCTION)
Shared Stateが変更したときに呼び出されるコールバック関数として、FUNCTIONを設定する
wave.getState().submitDelta({KEY: VALUE})
Shared StateにKEYに対応する値としてVALUEを設定する
wave.getState().get(KEY)
Shared StateからKEYに対応する値を取得する

全体の流れとしては、以下のとおりです。

  1. wave.getState().get(KEY)関数を使って変更された状態を処理するハンドラを作成する(例ではstateUpdated関数)
  2. 作成したハンドラをwave.setStateCallback(FUNCTION)関数でShared State変更時のコールバックに登録しておく
  3. 何らかのイベントが発生してwave.getState().submitDelta({KEY: VALUE})関数が呼び出されたときに全参加者のコールバック関数が実行される

では、前項の手順にしたがってWave上にガジェットを追加し、実行してみましょう。

図6 カウンターガジェット
図6 カウンターガジェット

ある参加者がボタンをクリックすると、全ての参加者のガジェットでカウンタの表示がインクリメントされました。このようにWaveガジェットでは Shared Stateを使って非常に簡単にCometを利用した参加者同士のリアルタイムなやりとりが実装できます。

そして、Shared Stateを使う利点はこれだけではありません。今度はWave上部の「Playback」⁠再生)ボタンを押下してみましょう。

図7 Playbackボタン
図7 Playbackボタン

ツールバーが変更され、巻き戻し・早送りボタンとスライダが表示されました。スライダを移動したりボタンを押下するとWaveの状態が巻き戻されて、今回の場合であればカウンタの値が減って行きます。このようにWaveではShared Stateの変更はすべて履歴に保存されていて、ガジェット開発者がなにも意識しなくてもPlayback機構は正しく動作します。

まとめ

基本的なガジェットの作成方法は以上のようになります。いかがでしょうか? iGoogle、OpenSocialなどでガジェットを作成したことのある人であれば、リアルタイム通信やプレイバックなどのWaveの機能を利用するWaveガジェットも非常に簡単に作れることが分かると思います。またガジェット未経験の方も、ガジェットは結局のところはHTML、CSS、JavaScriptなどの既存のWebページと同じ構成要素をXMLで包んだだけのものであって、特に心配する必要はないことが分かったでしょう。

Waveは現在デベロッパープレビューとして公開されていますが、Developer Sandboxもまだこちらのサイトでアカウントのリクエストが開始されたばかりで、実際にアクセスできる人のほとんどいない、謎の多いサービスだと思います。 本連載がその疑問に少しでも答えられるものになっていると嬉しく思います。

次回はもう一つのExtensions、Robots APIについて説明します。お楽しみに。

おすすめ記事

記事・ニュース一覧