XMPPライブラリ「Smack」

SmackはJavaプログラムで「XMPP(eXtensible Messaging and Presence Protocol)」によるメッセージングを行うためのオープンソースのライブラリである。XMPPはXMLベースのメッセージングプロトコルで、もともとJabber社製のインスタントメッセンジャー(IM)である「Jabber」で利用されており、その後IETFによる標準プロトコルとして認定された経緯から、「Jabber」または「Jabberプロトコル」の名称でも知られている。Jabber自身がすでにオープンソース化されている他、「Googleトーク」をはじめとする大手のIMサービスでも採用されていることから、XMPPは現在では業界の標準的なメッセージングプロトコルとして認識されている。

SmackはJavaで作成されたXMPPライブラリであり、JavaプログラムにおいてXMPPを利用するための標準的な機能を提供してくれるものだ。これを利用することで、開発者はXMPPのプロトコルや低レイヤの仕組みを意識することなく、メッセージングを利用するアプリケーションを作成することが可能となる。XMPPの標準仕様に準拠しているため、XMPPを採用するさまざまなIMサービスにこれひとつでアクセスできる。特にGoogle Talkを利用できるというのは大きなメリットと言えるだろう。

SmackはApache Software Licenseの下で公開されており、現在の最新版はバージョン3.1.0となっている。最終リリースが2008年と少々古いライブラリではあるが、手軽にXMPPを利用したいという開発者にとっては有用なツールである。

Smackを利用し、Googleトークでチャットを行う

Smackの最新版はこのページよりダウンロードすることができる。ダウンロードしたアーカイブには4つのJARファイルと、javadocを含むドキュメントが格納されている。そのうち、Smackのコアはsmack.jarであり、smackx.jarにSmack Extensionsと呼ばれる拡張機能が含まれている。したがって、通常はsmack.jarのみクラスパスに含めて利用すればよい。

ここではSmackを利用してXMPPサーバ(今回はGoogle Talkを利用する)にログインし、オンラインの相手(buddy)と行う方法について解説する。サーバへの接続にはorg.jivesoftware.smack.XMPPConnectionクラスを利用する。XMPPConnectionのインスタンスを生成するには、コンストラクタにサーバ名か、または接続に関する設定を保存したorg.jivesoftware.smack.ConnectionConfigurationオブジェクトを渡す。接続はconnect()メソッドで行い、接続に成功したら続いてlogin()メソッドを用いてログインを実施する。サーバへのログインを行うコード例を以下に示す。

リスト1

private XMPPConnection connection = null;

/* サーバへの接続とログイン */
public void connect(String server, String username, String password) {
    try {
    // 接続の設定
    SmackConfiguration.setPacketReplyTimeout(5000);
    ConnectionConfiguration config = new ConnectionConfiguration(server);
    config.setSASLAuthenticationEnabled(false);

    // サーバに接続してログインする
    this.connection = new XMPPConnection(config);
    this.connection.connect();
    this.connection.login(username, password);
    } catch (XMPPException ex) {
    ex.printStackTrace();
    }
}

このconnect()メソッドでは、まずorg.jivesoftware.smack.SmackConfigurationクラスでタイムアウト時間を設定している。そして接続するサーバ名を指定してConnectionConfigurationオブジェクトを生成し、SASLをオフに設定する。続いてXMPPConnectionを利用して接続とログインを実施する。

続いて、オンラインのbuddyとのチャットを行ってみる。チャットを扱うクラスはorg.jivesoftware.smack.Chatであり、このインスタンスはorg.jivesoftware.smack.ChatManagerクラスのcreateChat()メソッドを使って生成する。このメソッドにはチャット相手となるbuddyのIDと、buddyからメッセージを受け取った際にそれをハンドリングするリスナーオブジェクトを指定する。

リスナはorg.jivesoftware.smack.MessageListenerインタフェースを利用して実装する。このインタフェースにはprocessMessage()というメソッドが定義されており、これがメッセージを受け取った際に呼び出されるコールバック・メソッドとなる。次に示すchatOpen()メソッドは、引数に指定されたbuddyのIDに対してチャットを開始する例を示している。

リスト2

private Chat chat = null;

/* チャットの開始 */
public void chatOpen(String buddyId) {
    // ChatManagerを取得し、チャットを開始する
    ChatManager chatManager = this.connection.getChatManager();
    this.chat = chatManager.createChat(buddyId, new MessageListener() {
            /* メッセージを受信したら呼び出される */
            public void processMessage(Chat chat, Message message) {
                System.out.println(chat.getParticipant() + ": " + message.getBody());
            }
        });
}

processMessage()にはChatオブジェクトとorg.jivesoftware.smack.packet.Messageオブジェクトが渡される。Chatオブジェクトにはチャット相手に関する情報などが格納されており、この例のようにgetParticipant()メソッドを使うと相手のIDがわかる。Messageオブジェクトにはメッセージに関する情報が格納されている。メッセージの本文を取り出したい場合にはgetBody()メソッドを使用する。

createChat()メソッドはChatインスタンスを生成して返すので、メッセージ送信などアクションはこれを利用して行う。単にテキストを送信するだけであれば、ChatクラスのsendMessage()メソッドに文字列を渡せばよい。その他に、Messageオブジェクトを生成して渡す方法もある。以下のコードは、引数に渡された文字列をchatのsendMessage()に渡すことでbuddyに送信する例を示している。

リスト3

/* メッセージの送信 */
public void sendMessage(String message) {
    try {
    this.chat.sendMessage(message);
    } catch (XMPPException ex) {
    ex.printStackTrace();
    }
}

接続を解除したい場合には、XMPPConnectionのdisconnect()メソッドを呼び出せばよい。以上が、Smackを用いてXMPPサーバ経由でチャットを行う一連の流れである。以下に、指定した相手と実際にチャットを行うことができるプログラムの例を示す。このプログラムを実行すると、特定のbuddyとチャットを開始し、コマンドラインからテキストを入力することでメッセージを送ることができる。相手から受け取ったメッセージはコマンドラインに表示される。

リスト4

public class FirstSmackSample {
        private XMPPConnection connection = null;
        private Chat chat = null;
        private boolean isRunning = true;

        /* サーバへの接続とログイン */
        public void connect(String server, String username, String password) {
        // 前述の通り
        }

        /* チャットの開始 */
        public void chatOpen(String buddyId) {
            // 前述の通り
        }

        /* メッセージの送信 */
        public void sendMessage(String message) {
            // 前述の通り
        }

        /* チャットが継続中か否か */
        public boolean isRunning() {
            return this.isRunning;
        }

        /* チャットの終了 */
        public void chatClose() {
            this.isRunning = false;
        }

        /* 接続の終了 */
        public void destroy() {
            this.connection.disconnect();
        }

        public static void main(String[] args) {
            FirstSmackSample xmpp = new FirstSmackSample();
            // 接続してチャットを開始
            xmpp.connect("gmail.com", "[USERNAME]", "[PASSWORD]");
            xmpp.chatOpen("[BUDDYNAME]@gmail.com");

            // メッセージの送信処理
            while(xmpp.isRunning()) {
                try {
                    BufferedReader reader =
                            new BufferedReader(new InputStreamReader(System.in));
                    String message = reader.readLine();
                    if ("@close".equals(message.trim())) {
                        xmpp.chatClose();
                    } else {
                        xmpp.sendMessage(message);
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }

            // 終了
            xmpp.destroy();
        }
    }

main()メソッドではconnect()およびchatOpen()を呼び出して接続を確立し、isRunningフラグがtrueの間チャットを続ける。"@close"が入力されるとフラグをfalseにして接続を解除する。[USERNAME]、[PASSWORD]、[BUDDYNAME]にはXMPPサーバのユーザ名とパスワード、チャット相手のbuddy IDを指定する。サーバはGoogleトークを利用する場合はgmail.comになるが、他のXMPPサーバでもやり方は同じである。

FirstSmackSample側の実行結果は次のようになる。「[BUDDYNAME]@gmail.com:」となっているのが相手から送られてきたメッセージだ。相手側(Pidgenを利用)のログは図2.6のようになっており、会話が成立していることを確認できる。

プロンプト1

こんにちは
[BUDDYNAME]@gmail.com: 今日は
[BUDDYNAME]@gmail.com: いい天気ですね
Smackから接続しています
日本語も送れます
[BUDDYNAME]@gmail.com: 日本語、読めますか?
OKです
では、また
@close

相手側のチャットログ