Receiving PEP Broadcast

In my last blog, I talk about PEP and how you can publish your own PEP events with
Smack. In this blog I will talk about how to hand incoming PEP
messages. First we look at how a PEP packet looks like.

<event xmlns=”http://jabber.org/protocol/pubsub#event”&gt;
   <items node=”urn:xmpp:gaming:0″>

      <item id=”L5xpMkzWopMPK2AtuBUj”>
  
      <game xmlns=”urn:xmpp:gaming:0″>
         <character_name>Sentor</character_name>
         <character_profile>http://wow.example.com/profile.html?123456</character_profile&gt;
         <level>66</level>
         <name>Worlds of Warefare</name>
         <server_address>wow6.example.com</server_address>
         <server_name>WOW Example</server_name>
         <uri>http://wow.example.com</uri&gt;
      </game>
   </item>
   </items>
</event>

The message that we are interested in is in orange. We need to do 2 things to get to this part of the message

  1. Write a parser or provider for <game>. The parser will convert the XML fragment to Game object (see last weeks blog).
  2. Register the provider with Smack’s provider framework so that our provider gets call whenever there is a <game> tag. (actually its not like that but its easier to think of it this way…)

Writing a Provider

A provider is basically a parser for the bits in orange and it returns a GameItems object. Here is how our GameProvider looks like

public class GameProvider implements PacketExtensionProvider {
   public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
      boolean stop = false;
      int evtType;
      String n;
      String id = null;
      Game game = new Game();
      while (!stop) {
         evtType = parser.next();
         n = parser.getName();
         switch (evtType) {
            case XmlPullParser.START_TAG:
               if (“item”.equals(n))
                  id = parser.getAttributeValue(“”, “id”);
               else if (“character_name”.equals(n))
                  game.setCharacterName(text(parser));
               else if (“character_profile”.equals(n))
                  game.setCharacterProfile(new URI(text(parser)));
              
               break;
            case XmlPullParser.END_TAG:
               //Stop parsing when we hit </item>
               stop = “item”.equals(n);
               break;
         }
      }
      return (new GameItems(new GameItem(id, game));
   }
}

Things to note about

  1. A provider must implement the PacketExtensionProvider interface which has only one method, parseExtension()
  2. Your parser is responsible for parsing all XMLs between the opening and closing <item> tag.
  3. Don’t forget to get the id from the <item> tag
  4. Once you are done, wrap the Game object in a GameItem and GameItems object.

What is the GameItems object? GameItems is responsible for wrapping our Game object inside an <event> viz. the blue bits in the XML fragment at the start of this blog. Furthermore GameItems must extend PEPEvent class. Here is an implementation

public class GameItems extends PEPEvent {
   private final GamePEPItem item;
   public GameItems(GamePEPItem i) {
      item = i;
   }
   public GamePEPItem getGameItem() {
      return(item);
   }
   @Override
   public String getNamespace() {
      return (“http://jabber.org/protocol/pubsub#event&#8221;);
   }
   public String getNode() {
      return (“urn:xmpp:gaming:0”);
   }
   public String getItemDetailsXML() {
      return (item.toXML());
   }
   @Override
   public String toXML() {
      StringBuilder sb = new StringBuilder(“<event xmlns=\””);
      sb.append(getNamespace()).append(“\”><items node=\””);
      sb.append(getNode()).append(“\”>”);
      sb.append(getItemDetailsXML());
      sb.append(“</items></event>”);
      return (sb.toString());
   }
}

I know it is a bit confusing with layers of objects, hopefully the following illustration will help clarify this

<event xmlns=”http://jabber.org/protocol/pubsub#event”&gt; <– GameItems extends PEPEvent
   <items node=”urn:xmpp:gaming:0″> <– generated from GameItems
      <item id=”L5xpMkzWopMPK2AtuBUj”> <– GameItem extends PEPItem
         <game xmlns=”urn:xmpp:gaming:0″> <– Game
           
         </game>
      </item>
   </items>
</event>

Registering and Receiving PEP Events

In order to receive PEP messages, we must register our interest by
providing a provider (parser) and the namespace associated with the
provider. Here is a code snippet to show how this is done

PEPProvider prov = new PEPProvider();
prov.registerPEPParserExtension(“urn:xmpp:gaming:0”, new GameProvider());
ProviderManager.getInstance().addExtensionProvider(“event”, “http://jabber.org/protocol/pubsub#event&#8221;, prov);

PEPProvider is part of the PEP framework that allows you to indicate you interest in some or all PEP objects. Line 2 associates User Gaming namespace with our provider; you can register your interest in more that one namespace. The next step is to add the PEPProvider
to the overall Smack provider framework. Here the registration is very
specific, boilerplate in fact; you basically tell Smack that your
instance PEPProvider are taking over control of parsing any <event> tag that is in http://jabber.org/protocol/pubsub#event namespace
(line 3). (Note: I’m not sure what the ramifications are if there are
other providers that are also interested in the same namespace.)

The final piece of code snippets show how to add listeners (implements PEPListener).

XMPPConnection conn = … //Create an instance of XMPPConnection
PEPManager pepMgr = new PEPManager(conn);
pepMgr.addPEPListener(this); //this being PEPListener

public void eventReceived(String from, PEPEvent pEvt) {
//Cast it to GameItems object. pEvt is return from GameProvider
GameItems items = (GameItems) pEvt;
Game game = items.getGameItem().getGame();

That’s it! Feebacks and happy hacking!

Advertisements

PEP(Personal Eventing Protocol) with Smack

Personal Eventing Protocol (aka PEP) is a really nice pubsub feature on the Jabber platform. It allows you to publish information regarding yourself to your friends in your roster, like what Facebook. The XMPP community calls this rich presence.

I’ve been wanting to play with this feature so I started a little project to look into this. I’ll publish what games I’m playing using a yet unapproved XMPP specs call User Gaming (XEP-196). There are really 2 pieces to this little exercise:

  1. The low level bit which includes
    • Setting up data in the correct XML document/fragment and then send this out via Smack
    • Configuring Smack with the correct parser for your XML document; this is call a provider in Smack terminology. Parsing is required on the recipient side
  2. Using the high level Smack API to publish and receive incoming PEP messages

This will be a 3 part blog with this part focusing on creating the PEP item and sending it.

Setup

Get the latest copy of Smack from the repository. You need to get the source because we will need to make 2 small changes to PEPItem.java. Add the keyword public to lines 36, 37 like so

public abstract String getNode();
public abstract String getItemDetailsXML();

then recompile and use the new smackx.jar.

Creating PEP Payload for Publishing

To publish a PEP item, we need to know the structure of the XML document; here is how a typical out going (viz. publish) looks like

<pubsub xmlns=”http://jabber.org/protocol/pubsub”&gt;
   <publish node=”urn:xmpp:gaming:0″>
      <item id=”m3GYKa7jNiXf800pJ8cr”>
         <game xmlns=”urn:xmpp:gaming:0″>
            <character_name>Sentor</character_name>
                …
        </game>

      </item>
   </publish>
</pubsub>

Our responsibility is to generate the bits in red and green. The <game> is User Gaming and <item> is the envelope. The following code snippet shows how we use JAXB to generate the <game> fragment:

@XmlRootElement(name=Game.ELEMENT_NAME, namespace=Game.NAMESPACE)
public class Game {
   public static final String ELEMENT_NAME = “game”;
   public static final String NAMESPACE = “urn:xmpp:gaming:0”;
   private String characterName;   
      …
   @XmlElement(name=”character_name”, namespace=Game.NAMESPACE)
   public String getCharacterName() {
      return characterName;
   }
   public void setCharacterName(String characterName) {
      this.characterName = characterName;
   }
      …
   public String toXML() {
      String result = “<game/>”;
      try {
         JAXBContext context = JAXBContext.newInstance(Game.class);
         Marshaller gameMarshaller = context.createMarshaller();
         //Do not generate the XML declaration
         gameMarshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
         StringWriter sw = new StringWriter();
         gameMarshaller.marshal(this, sw);
         result = sw.toString();
      } catch (Exception e) {
         Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, “toXML()”, e);
      }
      return (result);
   }
      …
}

I’ve used toXML() instead of toString() to produce the XML fragment; this is in keeping with the conventions used in the Smack library.

Next up, we need to create the <item> wrapper. The following code snippet shows this:

public class GameItem extends PEPItem {
   private final Game game = null;
   private final String id;
   public GameItem(String i, Game g) {
      super(i);
      id = i;
      game = g;
   }
   public String getId() {
      return (id);
   }
   @Override
   public String getNode() {
      return (Constants.Game.NAMESPACE);
   }   
   @Override
   public String getItemDetailsXML() {      
      return ((game != null)? game.toXML(): “<game/>”);
   }

Some salient points about GameItem class

  1. You MUST extend PEPItem. The reason is that when you publish a PEP item, the publish() will only publish PEPItem type object. That is why you must also make the modification to PEPItem.java file (above), otherwise you would not be able to publish anything!
  2. For some strange reason, PEPItem does not allow you to get access to the id. On some protocol like user gaming, you use the same id to cancel a previous item. So its a good idea to add a getId(). Smack also has a handy utility to generate id strings. See StringUtils.randomString().
  3. getNode() method returns the name of the node. In PEP, this is typically the same as the namespace of the object. See One Node Per Namespace.
  4. getItemDetailsXML() returns <game> document.

Here is a how everything fits together

<pubsub xmlns=”http://jabber.org/protocol/pubsub”&gt;
   <publish node=”urn:xmpp:gaming:0“>  <—- getNode()
      <item id=”m3GYKa7jNiXf800pJ8cr“> <—- id from GameItem/PEPItem
         <game xmlns=”urn:xmpp:gaming:0″>
            <character_name>Sentor</character_name> <— everything in blue from getItemDetailsXML()

                …
        </game>

     
</item>
   </publish>
</pubsub>

Publishing

To publish a PEP item, create an instance of PEPManager, and invoke publish().

//Create an instance of PEPManager from a XMPPConnection
XMPPConnection conn = …
PEPManager mgr = new PEPManager(conn);

//Create an instance of Game and GameItem
Game game = new Game();
game.setCharacterName(“Sentor”);
game.set…

GameItem item = new GameItem(StringUtils.randomString(7), game);

//Now publish it
mgr.publish(item);

My next blog will look into receiving PEP messages. Happy hacking.

Listing Supported PubSub Features

When you perform a ServiceDiscoveryManager.getItems() on a pubsub JID (see Making Sense of Services), the server returns 2 types of information:

  1. the identity (<identity>) of the JID as defined in the Service Discovery Identities. You use the information here to determine what exactly is the JID. If you are looking for a pubsub service, then the category should contain pubsub value.
  2. the feature (<feature>) that is supported by the identity. In the case of a pubsub, these can be features like ability to create/delete nodes, persist items, etc. A list of the pubsub features can be found here.

This is typically how the packet will look like

<iq id=”SOKMA-5″ to=”fred@localhost/Smack” from=”pubsub.localhost” type=”result”>
<query xmlns=”http://jabber.org/protocol/disco#info”&gt;
<identity category=”pubsub” name=”Publish-Subscribe service” type=”service”/>
<feature var=”http://jabber.org/protocol/pubsub”/&gt;
<feature var=”http://jabber.org/protocol/pubsub#collections”/&gt;
<feature var=”http://jabber.org/protocol/pubsub#config-node”/&gt;
<feature var=”http://jabber.org/protocol/pubsub#create-and-configure”/&gt;
<feature var=”http://jabber.org/protocol/pubsub#create-nodes”/&gt;


</query>
</iq>

Although Smack allows you to retrieve the list of identities by the DiscoveryInfo.getIdentities() method, there seems to be no provision for retrieving a list of supported features associated with an identity (see thread). While there is a getFeatures() method, this however is not public. The only way seem to be explicitly asking if a feature is supported (DiscoverInfo.containsFeature(String)); for example if you want to check if a pubsub service supports user created nodes, you do the following

DiscoverInfo info = mgr.discoverInfo(“pubsub_jid@xmpp_server.org”);
if (info.containsFeature(“http://jabber.org/protocol/pubsub#create-nodes&#8221;)) {
//do something

So to get a list of the features here is a utility method

public static List<DiscoverInfo.Feature> extractFeature(String s) throws Exception {
   List<DiscoverInfo.Feature> list = new LinkedList<DiscoverInfo.Feature>();
   QName var = new QName(“var”);
   ByteArrayInputStream bis = new ByteArrayInputStream(s.getBytes());
   XMLEventReader xer = XMLInputFactory.newFactory().createXMLEventReader(bis);
   out:
   while (xer.hasNext()) {
      XMLEvent evt = xer.nextEvent();
      switch (evt.getEventType()) {
  
      case XMLStreamConstants.START_ELEMENT:
            StartElement st = evt.asStartElement();
            if (“feature”.equals(st.getName().getLocalPart()))
               list.add(new DiscoverInfo.Feature(
            st.getAttributeByName(var).getValue()));
            break;
         case XMLStreamConstants.END_DOCUMENT:
            break out;
         default:
      } //switch
   } // while
   xer.close();
   bis.close();
   return (list);
}

and the following code snippet shows how to use this utility method

DiscoverInfo info = mgr.discoverInfo(“pubsub_jid@xmpp_server.org”);
List<DiscoverInfo.Feature> featureList = extractFeature(info.getChildElementXML());

%d bloggers like this: