Google App Engine for JavaでXMPPを使ってみる

XMPPとはGTalkなどで使われているXMLベースのプロトコル。そして、Google App Engineからも使えるようにAPIが提供されているらしいので試しに使ってみた。
http://code.google.com/intl/ja/appengine/docs/java/xmpp/overview.html

GAEで作ったアプリからメッセージを送信することも出きるし、受信することもできる。そして、今回ためしに作ってみたのはGtalkでメッセージを送ったらそれに対する返事をするという人工知能アプリだ。(最後のはうそ)

受信に使われるJID(XMPPの中で使われるアドレス)は

  • app-id@appspot.com

となる。app-idはアプリケーションのIDのこと(appengine-web.xmlに書いているアレ)そして、カスタムのアドレスも作れるみたいで、その場合は

  • anything@app-id.appspotchat.com

となり、anythingの部分は好きな文字を入れてよい。このアドレスへメッセージを投げても受け取る先は同じ。GAEは送信者のアドレスを選んで送信することができる。(ドキュメントにドメイン違うから注意って書いてたのに間違った。ドメインの部分にapp-idが付いただけじゃなくその後も「appspotchat.com」なんだね。)

さて、今回のアプリを作って動かすまでにやることはざっと以下の6つ

  1. appengine-web.xmlXMPPでメッセージを受信できるように設定
  2. 普通のHttpServletを継承したクラスを作成(これがメッセージを受信したときに動く)
  3. web.xmlに2で作成したServletを登録
  4. appspotにデプロイ
  5. Gtalkからapp-id@appspot.comを招待する。
  6. Gtalkからapp-id@appspot.com宛にメッセージを投げると返事が返ってくる。

1. ppengine-web.xmlXMPPでメッセージを受信できるように設定

以下の行を追加します

	<inbound-services>
		<service>xmpp_message</service>
	</inbound-services>

これは、applicationタグやversionタグと同じレベルに追加します。

2. 普通のHttpServletを継承したクラスを作成(これがメッセージを受信したときに動く)

こんな感じ。

package sample.controller.xmpp;

import java.io.IOException;

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

import com.google.appengine.api.xmpp.JID;
import com.google.appengine.api.xmpp.Message;
import com.google.appengine.api.xmpp.MessageBuilder;
import com.google.appengine.api.xmpp.SendResponse;
import com.google.appengine.api.xmpp.XMPPService;
import com.google.appengine.api.xmpp.XMPPServiceFactory;

@SuppressWarnings("serial")
public class XMPPReceiverServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        XMPPService xmpp = XMPPServiceFactory.getXMPPService();
        Message message = xmpp.parseMessage(req);
        
        // メッセージを受け取った
        JID fromJid = message.getFromJid();
        String body = message.getBody();
        
        // 返事を返す
        String msgBody = "...zzz";
        if ("hello".equals(body)) {
            msgBody = "Hello " + message.getFromJid();
        } else if ("かっこいいね".equals(body)) {
            msgBody = "でしょー";
        }
        Message msg = new MessageBuilder()
            .withRecipientJids(fromJid)
            .withBody(msgBody)
            .build();
        
        boolean messageSent = false;
        if (xmpp.getPresence(fromJid).isAvailable()) {
            SendResponse status = xmpp.sendMessage(msg);
            messageSent = status.getStatusMap().get(fromJid) == SendResponse.Status.SUCCESS;
        }
        if (messageSent) {
            System.out.println("メッセージを送信しました");
        } else {
            System.out.println("メッセージの送信に失敗しました");
        }
    }

}

xmpp.parseMessage(req);の部分でHttpServletRequestからメッセージ内容を解析してcom.google.appengine.api.xmpp.Messageに変換しています。それこからメッセージボディの内容をみて、どんなメッセージを送り返すか決めてます。その後、送信するためにメッセージを生成して、xmpp.sendMessage(msg)でメッセージ送信者に送り返します。

3. web.xmlに2で作成したServletを登録

こんな感じ。

	<servlet>
		<servlet-name>xmppreceiver</servlet-name>
		<servlet-class>sample.controller.xmpp.XMPPReceiverServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>xmppreceiver</servlet-name>
		<url-pattern>/_ah/xmpp/message/chat/</url-pattern>
	</servlet-mapping>

app-id@appspot.comに送られたメッセージは「/_ah/xmpp/message/chat/」ここにくることになっているようです。なので、url-patternはこうなっています。

4. appspotにデプロイ

デプロイします。ローカル環境ではメッセージの送受信は実際には行うことが出来ません。送信してもコンソールにメッセージの内容が出てくるだけで寂しいです。

5. Gtalkからapp-id@appspot.comを招待する。

今デプロイしたアプリのJID(app-id@appspot.com)を招待します。招待したらアプリは勝手に承認するので、特にやることはないです。すぐにapp-id@appspot.comがオンライン状態になって現れるはずです。

6. Gtalkからapp-id@appspot.com宛にメッセージを投げると返事が返ってくる。

そして、Gtalkからapp-id@appspot.com宛にメッセージを投げてみましょう。こんな感じで人工知能らしからぬ反応が返ってきますよっと。

 me: hello
 app-id:  Hello <JID: aaaaaa@gmail.com/gmail.xxxxxx>
 me:  かっこいいね
 app-id u:  でしょー

いや、簡単。これはおもしろい。これだけでおもしろいアプリが出来そうだね。でも、俺アイディアないからなかなかアレだけど。何か作りたくなるそんなXMPPでした。

ローカルでテスト

あと、ローカルでテストするには以下のURLにアクセスしXMPPメッセージを送信することが出来ます。

このアプリをテストするにはFromとToに「app-id@appspot.com」を入力、メッセージも適当に入力して「送信」を押します。するとServletが呼ばれて以下のような感じでコンソールにログが出力されます。ローカルではこの内容を取得してチェックするのかな。送られたメッセージはテストの時取得できるのかなー。

Sending an XMPP Message:
    Body:
        Hello <JID: app-id@appspot.com>
    Type:
        chat
    RawXml:
        false
    To JIDs:
        app-id@appspot.com
メッセージを送信しました