簡単な通知アプリを作りたかったので、しばらく Google Talk の仕様とアプリからの呼出方法について調べていました。Google Talk は XMPP を使っていると言うことが分かったので、 下記の Smack API という Java のライブラリを使ってメッセージを送るアプリを作ってみました。面倒な部分はライブラリが引き受けてくれるので簡単に実現できます。
Ignite Realtime: Smack API
Smack is an Open Source XMPP (Jabber) client library for instant messaging and presence. A pure Java library, it can be embedded into your applications to create anything from a full XMPP client to simple XMPP integrations such as sending notification messages and presence-enabling devices.
同種のアプリとしては先日 Twitter につぶやきを送る簡単なアプリを書きましたが、同じような感じで使うことができますね。
Twitterは書いた内容が無条件に公開されてしまうので、非公開にしたい通知はこちらを活用する予定です。
† サンプルコード
実際に作ったサンプルはこんな感じです。サンプルを起動する前に事前準備として、あらかじめ双方のアカウントで相手のアカウントをチャットのリストに登録し、双方から相手のステータスを確認できる状態にしておく必要があります。
プログラミング時にハマりやすいポイントとしては、ソースコード中にもコメントしていますがメッセージ送信に使う ID が Google Talk にログインするときの ID と一致しない場合があるということでしょうか。これは非gmailアカウント(例えば iGoogle だけに登録しているアカウント)にみられる現象で、内部的には 1abcdabcda234@id.talk.google.com のような物理名に読み替えられています。幸いにも追加リクエストは読み替え前の メールアドレスで送ることができ、追加リクエストが済んでしまえばアプリから物理名を取得できるので、Mapを作っておいて送信時に宛先を読み替えてやれば問題ありません。それ以外は特に難しいことはないように思います。
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
public class GoogleTalkClient {
public static final String HOST = "talk.google.com";
public static final int PORT = 5222;
public static final String SERVICE_NAME = "gmail.com";
private XMPPConnection connection;
Map<String,String> name2user;
public static void main(String[] args) throws Exception {
GoogleTalkClient gt = new GoogleTalkClient("example_from@gmail.com", "examplepassword");
// 追加リクエストを送る
//gt.sendSubscribe("example_to@gmail.com");
//Thread.sleep(300000);
gt.sendMsg("example_to@gmail.com", "おーい");
gt.disconnect();
}
public GoogleTalkClient(String loginId, String password) throws XMPPException {
// GoogleTalk サーバに接続
ConnectionConfiguration connConfig = new ConnectionConfiguration(HOST, PORT, SERVICE_NAME);
connection = new XMPPConnection(connConfig);
connection.connect();
// ログイン
connection.login(loginId, password);
// ユーザー名をIDに読み替えるためのMapを作成
// gmail以外のアカウントは内部的には 1abcdabcda234@id.talk.google.com のような名前になっているので、
// ユーザー名によっては正常にメッセージ送信できないことがある。
name2user = new HashMap<String,String>();
Roster roster = connection.getRoster();
Collection<RosterEntry> entries = roster.getEntries();
for (RosterEntry entry : entries) {
if( entry.getName() != null ){
System.out.println("Name:" +entry.getName()+" -> User:"+entry.getUser() + ", Type: " + entry.getType());
name2user.put(entry.getName().trim(), entry.getUser().trim());
}
}
// PacketListenerを設定 (このリスナを通してメッセージを受信する)
connection.addPacketListener(new PacketListener() {
@Override
public void processPacket(Packet p) {
if (p instanceof Message) {
Message msg = (Message) p;
System.out.println("PacketListener(Msg): "+msg.getFrom() + ": " + msg.getBody());
} else if (p instanceof Presence) {
Presence pre = (Presence) p;
System.out.println("PacketListener(Pre): "+pre.getFrom() + " -> " + pre.getTo()+ ": " + pre.getType());
} else {
System.out.println("PacketListener: "+p.getFrom() + ": " + p.toString());
}
}
}, null);
// 自分のステータスをオンラインに設定
Presence presence = new Presence(Presence.Type.available);
connection.sendPacket(presence);
}
public void sendSubscribe(String to) {
// 追加リクエストを送る
Presence subscribe = new Presence(Presence.Type.subscribe);
subscribe.setTo(to);
connection.sendPacket(subscribe);
}
public void sendMsg(String to, String message){
if( name2user.containsKey(to) ){
to = name2user.get(to);
}
// メッセージを送る
Message msg = new Message(to, Message.Type.chat);
msg.setBody(message);
connection.sendPacket(msg);
}
public void disconnect(){
// 自分のステータスをオフラインにする
Presence presence = new Presence(Presence.Type.unavailable);
connection.sendPacket(presence);
connection.disconnect();
}
}
† 参考
Using google talk from java example :: Andrej Koelewijn