Developing XMPP Components – Some Tips

In this final part I’ll wrap up this series on writing XMPP components with a few random tips that have I’ve discovered.

Difference between Component and AbstractComponent

In my previous blog, I mention that you can either create XMPP componentby implementing Component or by extending AbstractComponent. So what is the difference between these 2?

Lets look at Component first; it is an interface so you’ll have to do all the work. The Component interface have the usual meta-information (getName(), getDescription()) and lifecycle (initialize(), start(), shutdown()) methods. You will have to implement all these methods. The heart of Component is undoubtedly processPacket(); all stanzas bound for this component are routed to this method. It is the responsibility of the your component to discriminate what type of packet this is (IQ, Message or Presence) and handling them accordingly. The following code snippet shows the general structure of how a typical processPacket() looks like

public void processPacket(org.xmpp.packet.Packet packet) {
   Packet reply = null;
   if (packet instanceof org.xmpp.packet.Message) {
      //Handle message
      reply = …
   } else if (packet instanceof org.xmpp.packet.IQ) {
      //Handle IQ
      reply = …
   } else {
      //Handle Presence
      reply = …
   }
   if (null != reply)
      componentManager.sendPacket(this, reply);
}

AbstractComponent has a much richer functionality. It is both a framework and a runtime/container.

XMPP framework As a framework, it provides nice methods to handle stanzas like handleMessage(), handlePresence() so that you don’t have to worry about figuring out what each stanza is. AbstractComponent also have specialize methods for dealing with disco#info (handleDiscoInfo()), disco#items (handleDiscoItems()), IQ get (handleIQGet()), etc. There are also methods to advertise your component’s services; you do this by overrding  discoInfoFeatureNamespaces(), discoInfoIdentityCategory(), discoInfoIdentityCategoryType() and returning appropriate values. There are also lifecycle methods: preComponentStart(), postComponentStart(), etc.

Runtime environment AbstractComponent provides default implementation for some services like ping and last activity. It also creates a threadpool to dispatch request so that when you are handling a stanza eg. in handleMessage(), so you don’t have to worry about holding on to the message dispatching thread. The default threadpool size is 17.

Most of you will undoubtedly choose AbstractComponent over Component. One of the first and immediate advantage is that with AbstractComponent, you don’t have to worry about trying to figure out what type of message you are getting. All these is done for you. AbstractComponent also automatically handles some of the messages for you like disco#info. All this just makes it really easy for you to develop XMPP components.

There is a great write up for AbstractComponent. See “Component Developer Guide“.

A word about threads: when you use Whack to create XMPP component, Whack ‘engine’ wraps your Component or AbstractComponent in an ExternalComponent. ExternalComponent provides the infrastructure to connect to an XMPP server, does authentication and also routes stanzas between your component (Component.processPacket()) and the XMPP server (ComponentManager.sendPacket()). ExternalComponent also creates a threadpool and routes messages using thread from the pool. So if you are using AbstractComponent, then there is a double thread dispatch, the handling off of the stanza from ExternalComponent thread to AbstractComponent thread. While this is not really a big deal, it is IMHO not a really great way to utilize resources. You can tune the number of threads in AbstractComponent but not in ExternalComponent.

org.xmpp versus org.jivesoftware.smack API

If you have been working with Smack API, one of the things that you will quickly discover is that both Component and AbstractComponent do not use this API. It uses a simpler and more generic API, lets call it org.xmpp, that is based on dom4j. Lets look at their differences by way of an example; if you are using Smack, here is how you would construct a reply to a disco#info

DiscoverInfo.Identity id = new DiscoverInfo.Identity(“automation”, “Convert to uppercase”);
id.setType(“uppercase”);
DiscoverInfo discoInfo = new DiscoverInfo();
discoInfo.addIdentity(id);
discoInfo.addFeature(“urn:xmpp:uppercase”);

The Smack code snippet will produce the following fragment (actually it produces more than that, but I’m just concentrating on the disco#info bit)

<query xmlns=”http://jabber.org/protocol/disco#info”>
   <identity category=”automation” name=”Convert to uppercase” type=”uppercase”/>
   <feature var=”urn:xmpp:uppercase”/>
</query>

The following code snippet shows how to produce a similar fragment using org.xmpp

//Get an instance of Element typically from IQ.createRequest() or similiar methods
Element discoInfo = …
discoInfo.addElement(“identity”)
      .addAttribute(“category”, “automation”)
      .addAttribute(“type”, “uppercase”)
      .addAttribute(“name”, “Convert to uppercase”);
discoInfo.addElement(“feature”)
      .addAttribute(“var”, “urn:xmpp:uppercase”);

As you can see that although org.xmpp is simpler, generic and more lightweight. But you do have to know the XMPP protocol to construct the stanza. If you are are, like me, conversant in Smack API and do not wish to know the ins and outs of the protocol then you may have a problem because Component, AbstractComponent and ComponentManager relies on org.xmpp heavily.

So my solution is to convert org.xmpp objects to Smack objects for manipulation and then convert Smack objects back to org.xmpp objects for dispatching. The following code snippet shows how convert from org.xmpp to Smack

//Component.handleMessage()
public void processPacket(org.xmpp.packet.Packet packet) {
   //First create a parser
   XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
   factory.setNamespaceAware(true);
   XmlPullParser parser = factory.newPullParser();
   parser.setInput(new StringReader(packet.toXML());
   //You MUST call next because Smack helpers assume that the first token
   //is ready to be read
   parser.next();

   //Decide what type of packet this is and call the appropriate helper
   //method from Smack package for parsing
   if (packet instanceof org.xmpp.packet.Message)
      org.jivesoftware.smack.packet.Message message =
            PacketParserUtils.parseMessage(parser);

   else if (packet instanceof org.xmpp.packet.IQ)
            …
}

The hard work is done by PacketParserUtils; determine what type of packet you are dealing with and then call parseMessage(), parsePresence() or parseIQ(). parseIQ() method seems to be missing from Smack 3.1. You will have to get the nightly builds to use it. One word of caution, org.xmpp and Smack API  share some classes with the same names like Message, IQ, Presence, etc. Just be aware which API you are using. One nice side effect of using Smack to parse your packets is that if you have installed Smack custom providers, PacketParserUtils will be able to pick them up.

After you have manipulated the Smack object, you want to convert it back to org.xmpp object before dispatching it. The following code snippet shows how this is done

//message is org.jivesoftware.smack.packet
Document doc = DocumentHelper.parseText(message.toXML());
org.xmpp.packet.Message toSend = new org.xmpp.packet.Message(doc.getRootElement());

To reverse the process, we get a stringified stanza (toXML()) of the Smack object, and parse that into a dom4j document. Then wrap that in a org.xmpp Message object to be send out. The DocumentHelper class is from dom4j.

I’ve not done any benchmarking or sizing as to the efficiency of this method. Let me know if you have any data on this. Maybe I’ll collect some data in the future.

Publishing component’s disco#info

After the component has successfully authenticated with the server, one of the first stanza that the server will be sending to your component will be a disco#info get. What the server is trying to get is more information about the services that your component is offering. If you fail to response to the disco#info, then the server might not list your component with the server receives a disco#item. This is the behaviour of Openfire.

Lets assume that we are going to return the above disco#info. Here is how you do it if you implementing Component

public void processPacket(Packet packet) {
   if (packet instanceof org.xmpp.packet.IQ) {
      IQ iq = (IQ)packet;
      Element iqElem = iq.getChildElement();
      if (“query”.equals(iqElem.getName()) &&
            “http://jabber.org/protocol/disco#info&#8221;.equals(iqElem.getNamespaceURI())) {
         IQ reply = IQ.createResultIQ(iq);
         Element element = reply.setChildElement(“query”
               ,
http://jabber.org/protocol/disco#info&#8221;);
            //Construct the reply as in previous section
            …
            //Send the reply
            componentManager.sendPacket(this, reply);

      } else // it is something else
         …
   }
}

However if you are extending AbstractComponent, this is how you would handle the server’s disco#info

@Override protected String[] discoInfoFeatureNamespaces() {
   return (new String[]{“urn:xmpp:uppercase”});
}
@Override protected String discoInfoIdentityCategory() {
     return (“automation”);
}
@Override protected String discoInfoIdentityCategoryType() {
   return (“uppercase”);
}

Until next time.

Developing XMPP Components – The Service

In the first part of this blog, I talked about XMPP component, server configurations (subdomain name and secret key) and also about setting up the build environment for Whack.

At the most basic level, developing XMPP components (XEP-0114) with Smack consists of the following Java classes

  1. A ‘main‘ class – This is the class that
    • Creates the XMPP component; this is the service
    • Connects and performs handshaking with a Jabber server
    • Keeps the XMPP component alive by going into an infinite loop
  2. The XMPP component class – This class either implements Component or extends AbstractComponent. The difference between the two is how much do you want to write viz. AbstractComponent provides a simple framework to handle various message type while Component you must implement all these yourself. I will go into this in greater detail in my next blog. For the time being, we will just go with Component

We will implement a simple uppercase service, where any message that you send to the service, the service will echo it back to you in uppercase. The following is the main class

import java.util.logging.*;
import org.jivesoftware.whack.*;
import org.xmpp.component.*;

public class Main {
   public static void main(String[] args) {

      ExternalComponentManager mgr = new ExternalComponentManager(“myserver”, 5275);
      mgr.setSecretKey(“landofthegiants”, “uppercase”);
      try {
         mgr.addComponent(“landofthegiants”, new UpperCaseComponent());
      } catch (ComponentException e) {
         Logger.getLogger(Main.class.getName()).log(Level.SEVERE, “main”, e);
         System.exit(-1);
      }
      //Keep it alive
      while (true)
         try {
            Thread.sleep(10000);
         } catch (Exception e) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, “main”, e);
         }
   }
}

Some salient points about the above code

  • You create an instance of ExternalComponentManager. This class will be responsible for all components connecting to a particular server. In this case its myserver. Note that 5275 is the standard port
  • What you need to do next is to associate a secret key (uppercase) with the subdomain (landofthegiants) of your component. The secret key and subdomain must first be defined on the XMPP server. See part 1.
  • Next you create an instance of your component and add that to ExternalComponentManager; the component is associated with the subdomain name. UpperCaseComponent must either implements Component or extends AbstractComponent.
  • Finally keep your ‘server’ alive with an infinite loop. 

The following class shows UpperCaseComponent

import org.xmpp.component.*;
import org.xmpp.packet.*;

public class UpperCaseComponent implements org.xmpp.component.Component {
    private ExternalComponentManager mgr = null;

    public String getName() {
        return (“Upper case”);
    }

    public String getDescription() {
        return (“Echos your message back to you in upper case”);
    }

    public void processPacket(Packet packet) {
        if (packet instanceof Message) {
            org.xmpp.packet.Message original = (Message) packet;
            org.xmpp.packet.Message response = original.createCopy();
            //Swap the sender/recipient fields
            response.setTo(original.getFrom());
            response.setFrom(original.getTo());
            //Convert the text to upper case
            response.setBody(original.getBody().toUpperCase());
            mgr.sendPacket(this, response);
        }
    }

    public void initialize(JID jid, ComponentManager componentManager) throws ComponentException {
        mgr = (ExternalComponentManager) componentManager;    
    }

    public void start() { }

    public void shutdown() { }
}

As you can see, implementing a component is quite simple. getName(), getDescription(), start() and shutdown() are pretty self explanatory. The initialize() method is called before start(). In the initalize() method, the component will be passed its JID; this will be the subdomain plus the domain. for example the JID of this component will be landofthegiants.myserver.com. The other thing that gets pass into the component via initialize() is the instance of the ComponentManager that created the component. Hold a reference of this because the component manager is the key to communicating with the outside world eg. sendPacket().

The processPacket() is the meat of the component; all packets to landofthegiants.myserver.com subdomain are routed to this method. So what you have to do is check if packet parameter is one of the 3 types of message: Message, IQ or Presence, and cast packet appropriately.

In the final part of my blog in this series, I’ll talk about using AbstractComponent, Smack versus org.xmpp API and advertising your component.

%d bloggers like this: