Making Sense of the Serivces

How do you know that a JID powwow.batcomputer is a conference service? Unless JIDs follow a certain convention, trying to programmatically determine a service from a name is not going to be sufficient (see last blog). What you really need to do is after you have gotten the services of the JID, you need to ask the server for more info about the JID by sending it a disco#info. Here is the code snippet that shows you how to do that

//disco#items
DiscoverItems items = mgr.discoverItems(HOST);
Iterator<DiscoverItems.Item> iter = items.getItems();

DiscoverInfo info = null;
while (iter.hasNext()) {
   DiscoverItems.Item i = iter.next();
   System.out.println(i.toXML());
   //Some servers do not return more info on a particular JID
   //will throw exception,
   try {
      //disco#info
      info = mgr.discoverInfo(i.getEntityID());
   } catch (Exception e) {
      continue;
   }
   Iterator<DiscoverInfo.Identity> idIter = info.getIdentities();
   while (idIter.hasNext()) {
      DiscoverInfo.Identity id = idIter.next();
      System.out.println(“\t ” + id.toXML());
   }
}

If we run the above on jabber.org, we get the following

<item jid=”proxy.eu.jabber.org”/>
<item jid=”conference.jabber.org” name=”Multi-User Chat”/>
         <identity category=”conference” name=”Isode M-Link Conferencing” type=”text”/>

The identity of conference.jabber.org is a conference and the type is text. As it turns out the entries the identity attributes are standard and they are defined in here (its one of the easiest specs to read). So we know from the specs that conference is a text based conferencing. However XMPP servers can also support IRC style chat, in which case type would be irc.

But I’m digressing. The whole purpose of my original exercise was to discover pubsub services. So I ran the above code on my openfire server and here’s what I got back

<item jid=”pubsub.batcomputer” name=”Publish-Subscribe service”/>
   <identity category=”pubsub” name=”Publish-Subscribe service” type=”service”/>

<item jid=”search.batcomputer” name=”User Search”/>
   <identity category=”directory” name=”User Search” type=”user”/>
<item jid=”conference.batcomputer” name=”Public Chatrooms”/>
   <identity category=”conference” name=”Public Chatrooms” type=”text”/>
  <identity category=”directory” name=”Public Chatroom Search” type=”chatroom”/>
<item jid=”proxy.batcomputer” name=”Socks 5 Bytestreams Proxy”/>
   <identity category=”proxy” name=”SOCKS5 Bytestreams Service” type=”bytestreams”/>

Note the pubsub category and the type which is a service.

Discovering Services

In my previous blog entry, I mentioned that you need to find out if a server supports pubsub. To do that you can use Pidgin’s XMPP Service Discovery Plugin. You can also use Smack to retrieve a list of supported services. Here is the code snippet that will retrieve a list of items/services that a server supports

//Connect and login
XMPPConnection conn = …

ServiceDiscoveryManager mgr = ServiceDiscoveryManager.getInstanceFor(conn);
//Assuming that we are querying jabber.org
DiscoverItems items = mgr.discoverItems(“jabber.org”);
Iterator<DiscoverItems.Item> iter = items.getItems();
while (iter.hasNext()) {
   DiscoverItems.Item i = iter.next();
   System.out.println(i.toXML());
}

This will produce the following from jabber.org

<item jid=”proxy.eu.jabber.org”/>
<item jid=”conference.jabber.org” name=”Multi-User Chat”/>

Once you have this list, you can now use the JID enteries to retrieve other services. For example, notice that jabber.org supports multiuser chat (MUC) with JID conference.jabber.org. We can now use this JID to get a list of rooms, configurations, etc.

Connecting to Google Talk produce the following

Exception in thread “main” feature-not-implemented(501)
   at org.jivesoftware.smackx.ServiceDiscoveryManager.discoverItems(ServiceDiscoveryManager.java:495)
   …

Clearly Google has implemented a no frills server; Google Talk only support 1 to 1 chat and nothing else. BTW to connect to Google talk, use the following code snippet

SASLAuthentication.supportSASLMechanism(“PLAIN”);
ConnectionConfiguration config = new ConnectionConfiguration(
      “talk.google.com”, 5222, “gmail.com”);
XMPPConnection conn = new XMPPConnection(config);
conn.connect();
//Note you MUST have ‘@gmail.com’ to login
conn.login(“gmail_id@gmail.com”, “your password”);

And finally here is the list of services from an instance of openfire running on my notebook

<item jid=”pubsub.batcomputer” name=”Publish-Subscribe service”/>
<item jid=”search.batcomputer” name=”User Search”/>
<item jid=”conference.batcomputer” name=”Public Chatrooms”/>
<item jid=”proxy.batcomputer” name=”Socks 5 Bytestreams Proxy”/>

Notice that pubsub JID which is exactly the service we were trying to connect with PubSubManager.

XMPP PubSub

I’ve been trying to get pubsub to work on Openfire and Smack for about a week. Finally I’ve managed to get it working this morning so I’ve decided to document seeing that it quite difficult to get all the info in 1 place. The solutions came from various place but in particular from ignite realtime forum and stackoverflow thread. So many thanks to those folks over there.

Here is my setup
1. Openfire server 3.6.4. Setup a few account for testing. Note: you can use any XMPP server but you have to make sure that the pubsub service is running. One way to do this is to use Pidgin, install the XMPP Service Discovery plugin and introspect the server. You should see something like this.

2. Smack from SVN. The release version does not have support for pubsub viz. there are nice classes like PubSubManager, etc. You will have to construct the pubsub stanza yourself.

Here is the code to create a publish an item

//Create a XMPPConnection
SASLAuthentication.supportSASLMechanism(“PLAIN”);
//For debugging
XMPPConnection.DEBUG_ENABLED = true;

ConnectionConfiguration config = new ConnectionConfiguration(HOST, PORT);
XMPPConnection conn = new XMPPConnection(config);
conn.connect();
conn.login(USER, PASSWORD);

//Create the topic
ConfigureForm f = new ConfigureForm(FormType.submit);
//Set some params for the topic – according to your requirement
f.setPersistentItems(false);
f.setDeliverPayloads(true);
f.setAccessModel(AccessModel.open);
f.setPublishModel(PublishModel.open);
f.setSubscribe(true);

//Note that you need to set the second parameter for openfire. This is the
//same as the pubsub service name
PubSubManager mgr = new PubSubManager(conn, “pubsub.” + HOST);
Node n = mgr.createNode(TOPIC, f);

//Now publish something – See Javadocs
SimplePayload payload = new SimplePayload(“book”,”pubsub:test:book”, “”);

PayloadItem payloadItem = new PayloadItem(null, payload);
((LeafNode)n).publish(payloadItem);

If you writing a real app, you would probably not use SimplePlayload but extend from org.jivesoftware.smackx.pubsub.Item.

Finally here is the code to subscribe to a topic

//mgr == PubSubManager
Node n = mgr.getNode(TOPIC);
n.addItemEventListener(new ItemEventListener() {
public void handlePublishedItems(ItemPublishEvent items) {
//Handle items
}
);
n.subscribe(conn.getUser());

Good luck!

Technorati Tags: , , , ,

%d bloggers like this: