サンプルのプログラムコード解説

2012年2月6日(月)
清野 克行
gaedirectで実現するクラウドWebサービス(スマートクラウド)

※この記事は、書籍『クラウドWebサービス入門 gaedirectによるHTML5連携とマッシュアップ』の内容を、Think IT向けに特別にオンラインで公開しているものです。詳しくは記事末尾の書籍紹介欄をご覧ください。

第1回では、クラウドWebサービスの意味と、サンプル多機能チャットの画面操作についてみてきましたが、今回はサンプルのプログラミングについて見ていきます。gaedirectを使用したクラウドWebサービスではサーバサイドでのコーディングを行う必要はなく、JavaScriptからのプログラム操作でクラウド(GAE)のデータストアやChanne APIなどの操作を行うことができます。またGoogle Mapsの操作や、HTML5での処理記述もJavaScriptから行われ、結局すべてのアプリケーション操作をブラウザ内のJavaScriptからの操作に集約することができる訳です。

図1:サンプルプログラムで構成されるクラウドWebサービス(クリックで拡大)

図1は第1回でも紹介しましたが、このサンプルでのソフトウェアモジュール構成になっています。図の左上がクラウドWebサービスを実現するgaedirectとそこからアクセスされるGoogle App Engineで、右上はマッシュアップで使用されるGoogle Maps、そして図の下部には、これ迄のブラウザクライアント機能を大きく拡張するHTML5のモジュール群が並びます。

結局、gaedirectでクラウドWebサーバを構成することによって、第1回で紹介したクラウド利用のメリットをすべて享受できる堅牢・安価で拡張性抜群のサーバ構成にすることができ、さらにサーバの構築・運用に費やされる工数も全く必要なくなるという、これ迄にないWebシステムにすることができます。かつ、そのWebシステムは、他のWebサービスとのマッシュアップ、およびHTML5との連携で、最小の工数で最強のWebアプリケーションを実現することができる訳です。

gaedirect2.0の基本機能解説と、gaedirectで実現するクラウドのWebサービス化と他のWebサービスとの組み合わせ、HTML5との組み合わせ、更にWebサービス+HTML5との組み合わせについては、現在発売中の書籍
『クラウドWebサービス入門 gaedirectによるHTML5連携とマッシュアップ』(秀和システム)
で様々なサンプルを取り上げて解説されています。
また、ここで紹介しているサンプルを実行するためには、gaedirect2.0が必要になりますが、上記書籍添付のCD-ROMに収納されており、書籍を購入して頂けばサンプルを実行できるようになります。

[I] 多機能チャットプログラム

ブラウザ対応:Chrome14.0/◯ Safari5.1/× Firefox6.0/× IE9/× Android/NA iPhone/NA

ここから、多機能チャットのプログラム解説を行っていきますが、ブラウザではChromeのみを対応ブラウザとしています。画面上での操作や表示については第1回の記事を参照して下さい。多機能チャットプログラムはプログラムコードがかなり長くなっていますので、ここからのプログラム解説は機能単位に分割して行っていきます。

リスト1 多機能チャットプログラムのJavaScript構成

[I] 画面ロード時の処理とイベント処理定義
  (A)Channel API 初期化
  (B)Web Storage への書き込みデータ表示
  (C)テキストのドラッグ&ドロップ
  (D)Channel API イベントハンドラの設定
  (E)ユーザインタラクション(ボタンクリック)への対応処理

[II] 関数定義
  (F)プッシュメッセージをバックグラウンドスクリプトからChannelサーバに送信
  (G)ローカルストレージへのアクセス
  (H)クラウドBigtable登録エンティティの参照
  (I)時間取得とフォーマット化

[III] 外部JavaScriptファイル
  (J)Google Mapsでのチャット参加者位置情報表示

リスト1は多機能チャットのJavaScriptコードを機能単位に列挙した内容になっています。解説ではこの内容を次のように分けて行っていきます。

    • (A)Channel API 初期化
    • (B)Channel API イベントハンドラの設定
    • (C)ユーザインタラクション(ボタンクリック)への対応処理
    • (D)Channel API イベントハンドラの設定
    • (E)ユーザインタラクション(ボタンクリック)への対応処理
    • (F)プッシュメッセージをバックグラウンドスクリプトからChannelサーバに送信
    • (G)ローカルストレージへのアクセス
    • (H)クラウドBigtable登録エンティティの参照
    • (I)時間取得とフォーマット化
    • (J)Google Mapsでのチャット参加者位置情報表示
    • (A)Channel API 初期化
    • (B)Channel API イベントハンドラの設定
    • (C)ユーザインタラクション(ボタンクリック)への対応処理
    • (D)Channel API イベントハンドラの設定
    • (E)ユーザインタラクション(ボタンクリック)への対応処理
  • リスト2 多機能チャットプログラム-1 (channelHtl5Gmap.htm)

      <!DOCTYPE html> 
      <html> 
      <head> 
      <meta charset="utf-8"/>
      <script type="text/javascript" src='/jslib/gaedirect.js'></script>
      <script type="text/javascript" src='/_ah/channel/jsapi'></script>
      <script type="text/javascript" src='/jslib/jquery-1.6.1.min.js'></script>
      <title>Channel API テスト</title>
      <script type="text/javascript">
      var token = "";
      var userId = "";
      var socket = "";
      var utime = "";
      var utime1 = "";
      var utime2 = "";
      var uname = "";
      var uemail = "";
      var umsg = "";
      var lat = 0.0;
      var lon = 0.0
      var udate = "";
      var initialized = false;
      var connected = false;
      var gettingToken = false;
      var listno = 0;
      var curlist = 0;
    
      var kind = "chnlhtml5";													//(1)
      var worker2 = new Worker("tworker.js");									//(2) 
      var db = window.openDatabase("lcheck","1.0","channelapi", 1048576); 		//(3)
      $(function(){ 
        rev_remote();															//(4)
        //(A) 開始:Channel API 初期化 
        clientId = ""+Math.floor(Math.random()*1000000);		//クライアントのユニークなID(clientId)を生成
        token = getToken(clientId);								//clientIdからトークン(token)を取得
        channel = new goog.appengine.Channel(token);			//tokenを使用してchannelオブジェクトを生成
        initialized = true;
        socket = channel.open();									//channelのsocketをオープンし、初期化完了
        $("#status").text("初期化が完了しました。serverからの応答待ちです。");
        //(A) 終了:Channel API 初期化
        //(B) 開始:Web Storage への書き込みデータ表示
        if(localStorage.uname != ""){											//(5)
          $("#uname").val(localStorage.uname);
        }
      
        if(localStorage.uemail != ""){											//(6)
          $("#uemail").val(localStorage.uemail);
        }
        //(B) 終了:Web Storage への書き込みデータ表示 
        //(C) 開始:テキストのDrag&Drop
        var tdest = document.getElementById("umsg");
        tdest.addEventListener("dragover", function(e) {						//(7)
          e.preventDefault();
        });
        tdest.addEventListener("drop", function(e) {							//(8)
          var tdat = e.dataTransfer.getData("text/plain");						//(9)
          var utmp = $("#umsg").val();											//(10)
          $("#umsg").val(utmp + " " + tdat);									//(11)
          e.stopPropagation(); 
        });
        //(C) 終了:テキストのDrag&Drop 
        //(D) 開始:Channel API イベントハンドラの設定 
        socket.onopen = function(){			//ソケット(Channel 通信)オープン時
          connected = true;
          $("#status").text("接続が開始されました。データ受信できます。");
        } 
        socket.onmessage = function(msg){				  //(12)プッシュメッセージ受信時
          /* ********************************************* *
          *    Get Deta from response event 
          * ********************************************* */
          var res = $.parseJSON(msg.data);										//(13)
          uname = res.uname;
          uemail = res.uemail;
          umsg = res.umsg;
          lat = res.lat; 
          lon = res.lon; 
          utime = res.utime;
          splist(--curlist, uname, uemail, lat, lon, setutime2(utime), umsg);	//(14)
                                                //プッシュ朱儁したメッセージを一覧表示に追加
        } 
        socket.onerror = function(err){					// Channel APIでエラー発生時
          $("#status").text("Serverでエラー発生。 内容:" + err.description + " エラー:" + err.code);
        }
      
        socket.onclose = function(){						// ソケット(Channel 通信)クローズ時
          $("#status").text("接続が正常に終了しました。");
          connected = false;
          initialized = false;
        } 
        //(D) 終了:Channel API イベントハンドラの設定
        //(E) 開始:ユーザインタラクション(ボタンクリック)への対応処理
        $("#send").click(function(){			//(15)serverにmessageを送るときに呼ばれる
          if(!initialized){ 
            open_channel();
          }
          if(!connected){ 
            $("#status").text("接続未完了でデータを送信できません");
          } 
          var showloc = $("input[name=showloc]:checked").val(); 
                                                    //位置情報表示の「OK」、「NO」を確認
          var lat = 0.0;
          var lon = 0.0;
          if ((showloc == "ok") && (navigator.geolocation)){		    //(16)
            navigator.geolocation.getCurrentPosition(			  //現在の位置情報を取得
              function (pos) { 
                lat = pos.coords.latitude;							//緯度取得
                lon = pos.coords.longitude;							//経度取得 
                up_to_channel(lat, lon);			//(17)クラウドアップロード関数呼び出し 
              }, 
              function (error) {							//位置情報の取得に失敗した場合
                var message = "";
                switch (error.code) { 
              case error.POSITION_UNAVAILABLE:				// 位置情報が取得できない場合
                $("#status").text("位置情報の取得ができませんでした。");
                break; 
              case error.PERMISSION_DENIED:			// Geolocationの使用が許可されない場合
                $("#status").text("位置情報取得の使用許可がされませんでした。");
                break; 
              case error.PERMISSION_DENIED_TIMEOUT:				  // タイムアウトした場合
                $("#status").text("位置情報取得中にタイムアウトしました。");
                break;
              }
              $("#status").text(message);
            }
          );
        }else{																  //(18)
          lat = -9999.99;						//位置情報を表示しない場合の仮の値(緯度)
          lon = -9999.99;						//位置情報を表示しない場合の仮の値(経度) 
          up_to_channel(lat, lon)											  //(19)
        } 
        localStorage.uname = $("#uname").val();		  //Webストレージ(KVS)への書き込み
        localStorage.uemail = $("#uemail").val();		//Webストレージ(KVS)への書き込み
      });
      worker2.onmessage = function(val) {   			//(20) ワーカから結果を受け取る
        $("#status").text("送信ステータス: " + val.data);
      };
      $("#close").click(function(){
        if(socket == "" || initialized == false || connected == false){
          $("#status").text("まだ接続されていません");
            return;
        }
        socket.close();												//ソケットのクローズ
        socket = "";
        initialized = false;
        connected = false;
        token = "";
        clientId = "";
        $("#status").text("接続を終了しました");
      }); 
      $("#splist").click(add_local);											//(21)
      $("#revLocal").click(rev_local);											//(22)
      $("#delLocal").click(del_local);											//(23)
      $("#revRemote").click(rev_remote);										//(24)
      var gmapwin;
      $("#revGmaps").click(function(){											//(25)
        gmapwin = window.open("13.3-gmapwin.htm", "window", "width=520,height=380,top=180"); 
      });
      //(E) 終了:ユーザインタラクション(ボタンクリック)への対応処理 
    });
    

    リスト2は[I]の 画面ロード時の処理とイベント処理定義に対応するプログラムコードですが、この部分もやや長いですので幾つかのブロックに分けて見ていきます。

    有限会社サイバースペース
    慶應義塾大学工学部電気科卒。日本IBM、日本HPなどにおいて、製造装置業を中心とした業務系/基幹業務系システムのSE/マーケティングや、3階層C/Sアーキテクチャによる社内業務システム開発などに携わる。現在は、Ajax/Web 2.0関連のセミナー講師/コンサルティング、書籍執筆などを行っている。情報処理学会会員。http://www.at21.net/

    連載バックナンバー

    データ解析技術解説

    サンプルのプログラムコード解説

    2012/2/6
    gaedirectで実現するクラウドWebサービス(スマートクラウド)
    データ解析技術解説

    クラウドWebサービスの概要とサンプル画面操作

    2012/1/31
    gaedirectで実現するクラウドWebサービス(スマートクラウド)

    Think ITメルマガ会員登録受付中

    Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

    Think ITメルマガ会員のサービス内容を見る

    他にもこの記事が読まれています