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

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構成

01[I] 画面ロード時の処理とイベント処理定義
02  (A)Channel API 初期化
03  (B)Web Storage への書き込みデータ表示
04  (C)テキストのドラッグ&ドロップ
05  (D)Channel API イベントハンドラの設定
06  (E)ユーザインタラクション(ボタンクリック)への対応処理
07 
08[II] 関数定義
09  (F)プッシュメッセージをバックグラウンドスクリプトからChannelサーバに送信
10  (G)ローカルストレージへのアクセス
11  (H)クラウドBigtable登録エンティティの参照
12  (I)時間取得とフォーマット化
13 
14[III] 外部JavaScriptファイル
15  (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)

    001  <!DOCTYPE html>
    002  <html>
    003  <head>
    004  <meta charset="utf-8"/>
    005  <script type="text/javascript" src='/jslib/gaedirect.js'></script>
    006  <script type="text/javascript" src='/_ah/channel/jsapi'></script>
    007  <script type="text/javascript" src='/jslib/jquery-1.6.1.min.js'></script>
    008  <title>Channel API テスト</title>
    009  <script type="text/javascript">
    010  var token = "";
    011  var userId = "";
    012  var socket = "";
    013  var utime = "";
    014  var utime1 = "";
    015  var utime2 = "";
    016  var uname = "";
    017  var uemail = "";
    018  var umsg = "";
    019  var lat = 0.0;
    020  var lon = 0.0
    021  var udate = "";
    022  var initialized = false;
    023  var connected = false;
    024  var gettingToken = false;
    025  var listno = 0;
    026  var curlist = 0;
    027 
    028  var kind = "chnlhtml5";                                                   //(1)
    029  var worker2 = new Worker("tworker.js");                                   //(2)
    030  var db = window.openDatabase("lcheck","1.0","channelapi", 1048576);       //(3)
    031  $(function(){
    032    rev_remote();                                                           //(4)
    033    //(A) 開始:Channel API 初期化
    034    clientId = ""+Math.floor(Math.random()*1000000);        //クライアントのユニークなID(clientId)を生成
    035    token = getToken(clientId);                             //clientIdからトークン(token)を取得
    036    channel = new goog.appengine.Channel(token);            //tokenを使用してchannelオブジェクトを生成
    037    initialized = true;
    038    socket = channel.open();                                    //channelのsocketをオープンし、初期化完了
    039    $("#status").text("初期化が完了しました。serverからの応答待ちです。");
    040    //(A) 終了:Channel API 初期化
    041    //(B) 開始:Web Storage への書き込みデータ表示
    042    if(localStorage.uname != ""){                                           //(5)
    043      $("#uname").val(localStorage.uname);
    044    }
    045   
    046    if(localStorage.uemail != ""){                                          //(6)
    047      $("#uemail").val(localStorage.uemail);
    048    }
    049    //(B) 終了:Web Storage への書き込みデータ表示 
    050    //(C) 開始:テキストのDrag&Drop
    051    var tdest = document.getElementById("umsg");
    052    tdest.addEventListener("dragover", function(e) {                        //(7)
    053      e.preventDefault();
    054    });
    055    tdest.addEventListener("drop", function(e) {                            //(8)
    056      var tdat = e.dataTransfer.getData("text/plain");                      //(9)
    057      var utmp = $("#umsg").val();                                          //(10)
    058      $("#umsg").val(utmp + " " + tdat);                                    //(11)
    059      e.stopPropagation();
    060    });
    061    //(C) 終了:テキストのDrag&Drop
    062    //(D) 開始:Channel API イベントハンドラの設定
    063    socket.onopen = function(){         //ソケット(Channel 通信)オープン時
    064      connected = true;
    065      $("#status").text("接続が開始されました。データ受信できます。");
    066    }
    067    socket.onmessage = function(msg){                 //(12)プッシュメッセージ受信時
    068      /* ********************************************* *
    069      *    Get Deta from response event
    070      * ********************************************* */
    071      var res = $.parseJSON(msg.data);                                      //(13)
    072      uname = res.uname;
    073      uemail = res.uemail;
    074      umsg = res.umsg;
    075      lat = res.lat;
    076      lon = res.lon;
    077      utime = res.utime;
    078      splist(--curlist, uname, uemail, lat, lon, setutime2(utime), umsg);   //(14)
    079                                            //プッシュ朱儁したメッセージを一覧表示に追加
    080    }
    081    socket.onerror = function(err){                 // Channel APIでエラー発生時
    082      $("#status").text("Serverでエラー発生。 内容:" + err.description + " エラー:" + err.code);
    083    }
    084   
    085    socket.onclose = function(){                        // ソケット(Channel 通信)クローズ時
    086      $("#status").text("接続が正常に終了しました。");
    087      connected = false;
    088      initialized = false;
    089    }
    090    //(D) 終了:Channel API イベントハンドラの設定
    091    //(E) 開始:ユーザインタラクション(ボタンクリック)への対応処理
    092    $("#send").click(function(){            //(15)serverにmessageを送るときに呼ばれる
    093      if(!initialized){
    094        open_channel();
    095      }
    096      if(!connected){
    097        $("#status").text("接続未完了でデータを送信できません");
    098      }
    099      var showloc = $("input[name=showloc]:checked").val();
    100                                                //位置情報表示の「OK」、「NO」を確認
    101      var lat = 0.0;
    102      var lon = 0.0;
    103      if ((showloc == "ok") && (navigator.geolocation)){            //(16)
    104        navigator.geolocation.getCurrentPosition(             //現在の位置情報を取得
    105          function (pos) {
    106            lat = pos.coords.latitude;                          //緯度取得
    107            lon = pos.coords.longitude;                         //経度取得
    108            up_to_channel(lat, lon);            //(17)クラウドアップロード関数呼び出し
    109          },
    110          function (error) {                            //位置情報の取得に失敗した場合
    111            var message = "";
    112            switch (error.code) {
    113          case error.POSITION_UNAVAILABLE:              // 位置情報が取得できない場合
    114            $("#status").text("位置情報の取得ができませんでした。");
    115            break;
    116          case error.PERMISSION_DENIED:         // Geolocationの使用が許可されない場合
    117            $("#status").text("位置情報取得の使用許可がされませんでした。");
    118            break;
    119          case error.PERMISSION_DENIED_TIMEOUT:               // タイムアウトした場合
    120            $("#status").text("位置情報取得中にタイムアウトしました。");
    121            break;
    122          }
    123          $("#status").text(message);
    124        }
    125      );
    126    }else{                                                                //(18)
    127      lat = -9999.99;                       //位置情報を表示しない場合の仮の値(緯度)
    128      lon = -9999.99;                       //位置情報を表示しない場合の仮の値(経度)
    129      up_to_channel(lat, lon)                                             //(19)
    130    }
    131    localStorage.uname = $("#uname").val();       //Webストレージ(KVS)への書き込み
    132    localStorage.uemail = $("#uemail").val();       //Webストレージ(KVS)への書き込み
    133  });
    134  worker2.onmessage = function(val) {               //(20) ワーカから結果を受け取る
    135    $("#status").text("送信ステータス: " + val.data);
    136  };
    137  $("#close").click(function(){
    138    if(socket == "" || initialized == false || connected == false){
    139      $("#status").text("まだ接続されていません");
    140        return;
    141    }
    142    socket.close();                                             //ソケットのクローズ
    143    socket = "";
    144    initialized = false;
    145    connected = false;
    146    token = "";
    147    clientId = "";
    148    $("#status").text("接続を終了しました");
    149  });
    150  $("#splist").click(add_local);                                            //(21)
    151  $("#revLocal").click(rev_local);                                          //(22)
    152  $("#delLocal").click(del_local);                                          //(23)
    153  $("#revRemote").click(rev_remote);                                        //(24)
    154  var gmapwin;
    155  $("#revGmaps").click(function(){                                          //(25)
    156    gmapwin = window.open("13.3-gmapwin.htm", "window", "width=520,height=380,top=180");
    157  });
    158  //(E) 終了:ユーザインタラクション(ボタンクリック)への対応処理 
    159});

    リスト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メルマガ会員のサービス内容を見る

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