仲間の状態の変化を検出する

今週も前回に引き続いてJava用IMライブラリ「Smack」の使用方法を紹介する。前回では、Smackを利用したJavaプログラムからGoogle Talkのサーバにログインし、仲間リストを取得して表示する方法を解説した。そのためにはRosterクラスを利用したが、SmackにはこのRosterの状態を監視して、変更があった場合に通知を受け取る方法が用意されている。これを利用すれば、仲間がオンライン/オフラインや、離席などの状態を、リアルタイムでユーザに提示することができるようになる。

Rosterの監視はorg.jivesoftware.smack.RosterListenerを登録することによって行う。RosterListenerのインタフェースには次の4つのメソッドが定義されている。

  • void presenceChanged(Presence) - Presenceが変更された場合に呼び出される
  • void entriesAdded(Collection<String>) - RosterEntryが追加された場合に呼び出される
  • void entriesUpdated(Collection<String>) - RosterEntryが更新された場合に呼び出される
  • void entriesDeleted(Collection<String>) - RosterEntryが削除された場合に呼び出される

presenceChanged()はPresenceオブジェクトの変更に関する通知を受け取るためのメソッドで、引数には変更されたPresenceそのものが渡される。「在席」や「離席」などの状態の変化を知りたい場合にはこのメソッドを実装すればよい。その他の3つのメソッドは、Rosterに格納されているRosterEntryに変更が加わった場合の通知を受け取るためのもので、引数には変更されたRosterEntryのアドレスのリストが渡される。たとえば、仲間が新たに追加された場合の通知はentriesAdded()メソッドによって受け取ることができる。

Rosterへのリスナの登録は、addRosterListener()メソッドによって行う。次のコードは、RosterListenerの実装クラスをリスナを登録する例である。このメソッドを実行した以降は、仲間の状態(Presence)が変わった場合にその旨がコンソールに表示されるようになる。

リスト1

/* 仲間リストを監視 */
public void startRosterWatching() {
    Roster roster = connection.getRoster();
    // リスナを登録してRosterの変更を監視する
    roster.addRosterListener(new RosterListener() {
            public void presenceChanged(Presence presence) {
                System.out.println(presence.getFrom() + "の状態が更新されました: " + presence);
            }

            public void entriesDeleted(Collection<String> addresses) {}
            public void entriesUpdated(Collection<String> addresses) {}
            public void entriesAdded(Collection<String> addresses) { }
        });
}

このメソッドを前回示したRosterSample.javaに追加し、main()メソッドを以下のように記述する。

リスト2

public static void main(String[] args) {
    RosterSample xmpp = new RosterSample();
    // サーバに接続
    xmpp.connect("gmail.com", "[USERNAME]", "[PASSWORD]");

    // 状態が反映されるまでの時間待ち
    try {
    Thread.sleep(5000);
    } catch (InterruptedException ex) {
    ex.printStackTrace();
    }

    // Roster監視のテスト
    xmpp.startRosterWatching();
    boolean isRunning = true;
    while(isRunning) {
        try {
            Thread.sleep(100);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

    // 切断
    xmpp.destroy();
}

実行結果は次のようになる。この例は、対象のユーザが[オンライン/在席]→[オンライン/離席]→[オフライン]→[オンライン/在席]と変わった場合の表示だ。

プロンプト1

xxxxxxxx@gmail.comの状態が更新されました: available: away (出かけています)
xxxxxxxx@gmail.comの状態が更新されました: unavailable
xxxxxxxx@gmail.comの状態が更新されました: available

自分の状態を設定する

自分自身の状態を任意に変更したい場合にはどうすればいいだろうか。その場合にもPresenceパケットを利用することができる。Presenceパケットは存在情報を通知するために利用するパケットであるため、このパケットに自身の存在情報を載せてサーバに送信すればいいわけだ。

Smackの場合は、まずPresenceクラスのインスタンスを作成し、そこにパケットのタイプやモード、ステータスを設定した上で、サーバに対してパケットを送信すればよい。Presenceのインスタンスは、コンストラクタにパケットのタイプを指定して呼び出すことで生成できる。モードとステータスの設定は、それぞれsetMode()およびsetStatus()メソッドによって行う。パケットの送信はXMPPConnectionのsendPacket()メソッドを利用して行う。次に示すsetStatus()メソッドは、指定されたタイプ、モード、ステータスでPresenceを作成し、それを使ってサーバにパケットを送信するコードの例を示している。

リスト3

/* 自身の状態をセットする */
public void setStatus(Presence.Type type, Presence.Mode mode, String status) {
    Presence presence = new Presence(type);
    presence.setMode(mode);
    presence.setStatus(status);

    this.connection.sendPacket(presence);
}

メソッドの呼出側は次のようになる。それぞれ「離席」「dnd」「オフライン」へ変更する場合の呼び出し例である。

リスト4

// "離席"に
xmpp.setStatus(Presence.Type.available, Presence.Mode.away, "席をはずしています");
// "dnd"に
xmpp.setStatus(Presence.Type.available, Presence.Mode.dnd, "いま手が離せません");
// オフラインに
xmpp.setStatus(Presence.Type.unavailable, null, null);

このユーザを仲間リストに登録している別のユーザから見れば、上記の3つの状態の変更によって、それぞれ図1、図2、図3のように表示が変化することを確認できる。

「離席」へ変化

「dnd」へ変化

「オフライン」へ変化

新しく仲間を追加する

仲間リストに新しく仲間を追加したい場合には、RosterクラスのcreateEntry()メソッドを利用する。このメソッドは、追加したいユーザのユーザIDと名前、そして所属させたいグループを指定して呼び出すことにより、Rosterに新しいRosterEntryを追加する。そして対象のユーザに対して仲間リストへの追加リクエストを送信する。グループはStringの配列として複数指定することができる。たとえば次のような具合である。

リスト5

Roster roster = ...;
String[] groups = {"仕事", "マイコミ"};
roster.createEntry("mycom", "マイコミ太郎", groups);

以下に示すaddRosterEntry()は、ユーザID、名前、グループを指定してRosterオブジェクトに新しいRosterEntryを追加するメソッドの例である。

呼び出し側は次のようになる。

リスト6

String[] groups = {"友人"};
xmpp.addRosterEntry("[USERNAME]@gmail.com", "[PASSWORD]", groups);

対象となったユーザに対しては、図3.4のように仲間リストへの追加リクエストが送られる。

仲間リストへの追加リクエストが送られる

以上、3回に渡ってSmackの基本的な使い方を紹介した。XMPPは汎用性の高いメッセージングプロトコルであるため、その全貌を理解するのはなかなか容易ではないが、Smackを利用すればプロトコルの複雑な部分をAPIが隠蔽してくれるため、非常に手軽にIMの機能を実装することができる。近年では専用クライアント以外でもIMの機能を持ったアプリケーションが増えてきている。そのようなアプリケーションを作る場合に、Smackは有力な候補となり得るだろう。