libro
www.tuyano.com
Google App Engine for Java(GAE/J)プログラミング入門

Channel APIによるクライアント=サーバー双方向通信 (5/5)

作成:2012-07-08 10:10
更新:2012-07-08 10:10

■すべてのチャンネルにメッセージを送る

1対1でのメッセージ送信はこれでわかりました。今度は、「接続しているすべてのチャンネルに対して送信をする」ということを考えてみましょう。

これには、実は特別な機能は用意されていません。ChannelServiceも、接続されている全セッションを取り出すような仕組みはないのです。というより、チャンネルそのものをJava内から管理するような形にはなっていないようです。

ではどうするのか。要するに、すべてのチャンネルに対して、メッセージが送れればよいのですね。メッセージは、ChannelServicesendMessageで送信します。このとき、送信先のチャンネルのチャンネルキーを指定してChannelMessageインスタンスを作ります。つまり、チャンネルキーさえわかれば、どのチャンネルにも送ることができるわけです。

あらかじめ、どこかに配列やコレクションを作成しておき、createChannelでチャンネルを作成する度にそのチャンネルキーを保管しておくようにします。そしてメッセージを送信するときは、繰り返しを使い、保管してあるチャンネルキーを順番に取り出してはsendMessageしていけばいいわけです。

では、先ほどのサンプルを修正してみましょう。下のリスト欄に2つのリストを掲載しておきました。HTMLファイルにアクセスするとチャンネルを作成し、トークンのボタンを押すと通信を開始する、という点は同じです。が、入力フィールドにメッセージを書いて送信すると、今度はチャンネルで接続されているすべてのクライアントにメッセージが配信されるようになります。

ここではChannelServiceでcreateChannelをする際、ArrayListにチャンネルキーを追加しておき、メッセージの送信時にはArrayListに保管されているすべてのチャンネルキーに送信をするようにしています。このようにすれば、簡単に全チャンネルへの送信が行えます。


――これで、Channel APIを使った双方向通信の基本がだいたいわかりました。後は、それをどのように応用していくか、というだけです。ここでのサンプルで、既に簡単なチャットシステムの原型はできていますから、少し手を加えればより本格的なメッセージサービスが作れることになるでしょう。それぞれで試してみて下さい。

GAEでのチャンネル利用数は、無料枠内では同時に100チャンネルまでです。有料にすると、デフォルトで設定される1日最大2ドルまでの枠内で、95,040まで作成可能になります(追加の支払いでどんどん増やせます)。

現状では、作成したチャンネルを随時終了するうまい方法は用意されていないようです(クライアント側で開いたSocketをcloseしても、すぐにはチャンネルが閉じられないようです)。ですので、本格的に利用するならGAEを有料サービスに切り替えることを考えたほうがよいかも知れません。

※プログラムリストが表示されない場合

AddBlockなどの広告ブロックツールがONになっていると、プログラムリスト等が表示されない場合があります。これらのツールをOFFにしてみてください。

●プログラム・リスト●

※サーブレットのコード

package com.tuyano.libro;

import com.google.appengine.api.channel.ChannelMessage;
import com.google.appengine.api.channel.ChannelService;
import com.google.appengine.api.channel.ChannelServiceFactory;

import java.io.IOException;
import java.util.ArrayList;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@SuppressWarnings("serial")
public class MyGaeAppServlet extends HttpServlet {
    static final String keyListName = "keyList";

    public void doGet(HttpServletRequest request, 
            HttpServletResponse response)
            throws IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/plain;charset=utf-8");
        ChannelService channelService = ChannelServiceFactory
                .getChannelService();
        String channelKey = request.getParameter("channelKey");
        String token = channelService.createChannel(channelKey);
        response.getWriter().println(token);
        ServletContext app = this.getServletContext();
        ArrayList<String> keyList = (ArrayList<String>)app.getAttribute(keyListName);
        if (keyList == null) {
            keyList = new ArrayList<String>();
        }
        keyList.add(channelKey);
        app.setAttribute(keyListName, keyList);
    }

    public void doPost(HttpServletRequest request, 
            HttpServletResponse response)
            throws IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/plain;charset=utf-8");
        ChannelService channelService = ChannelServiceFactory
                .getChannelService();
        String channelKey = request.getParameter("channelKey");
        String message = request.getParameter("message");
        ServletContext app = this.getServletContext();
        ArrayList<String> keyList = (ArrayList<String>)app.getAttribute(keyListName);
        if (keyList != null) {
            for (String key : keyList) {
                channelService.sendMessage(new ChannelMessage(key, message));
            }
        }
        return;
    }
}


※HTMLファイルのコード

<html>
  <head>
    <style type="text/css">
      h1 {
        font-size:14pt;
        background-color:FFDDFF;
      }
    </style>
  </head>
  <body>
    <script src='/_ah/channel/jsapi'></script>
    <script>
      var channel;
      var channelKey = "KEY" + new Date().getTime();
      var socket;
      var connected;
      
      function createChannel(path){
        var xhr = new XMLHttpRequest();
        xhr.open('GET', path + '?channelKey=' + channelKey, true);
        xhr.onreadystatechange = function(){
          if (xhr.readyState == 4 && xhr.status == 200){
            createChannelCallback(xhr);
          }
        };
        xhr.send();
      };
      
      function createChannelCallback(request){
        var token = document.querySelector('#token');
        token.value = request.responseText;
      };
      
      function sendMessage(path, msg) {
        var xhr = new XMLHttpRequest();
        xhr.open('POST', path + '?channelKey=' + channelKey +
                 '&message=' + msg, true);
        xhr.send();
      };
      
      var onOpened = function() {
        connected = true;
        alert("opened!");
      };
      var onMessage= function(param) {
        var ul = document.querySelector('#list');
        ul.innerHTML = '<li>' + param.data + '</li>' + ul.innerHTML
          };
      var onError = function(e){
        alert("error:[" + e.code + "]" + e.description);
      };
      var onClose = function() {
        connected = false;
        alert("close");
      };
      
      function openChannel() {
        var token = document.querySelector('#token').value;
        channel = new goog.appengine.Channel(token);
        socket = channel.open();
        socket.onopen = onOpened;
        socket.onmessage = onMessage;
        socket.onerror = onError;
        socket.onclose = onClose;
      }
      
      function initialize() {
        createChannel('mygae');
        document.body.onunload = function(){
          try {
            socket.close();
          } catch(e){}
        }
      }
      
      function doAction(){
        var msg = document.querySelector('#msg').value;
        sendMessage("mygae", msg);
      } 
      
      setTimeout(initialize, 100);
    </script>
    <h1>
      sample
    </h1>
    TOKEN:<input type="text" id="token">
    <button onclick="openChannel();">click</button>
    <hr>
    MESSAGE:<input type="text" id="msg">
    <button onclick="doAction();">click</button>
    <hr>
    <ul id="list"></ul>
  </body>
</html>


※関連コンテンツ

「Google App Engine for Java(GAE/J)プログラミング入門」に戻る