Need direction on parsing info from a packet

To start with, I’m implementing pubsub functionality using raw packets (long story short: I cannot download the extension for PubSub that would make my life easier).

Here is the way I’m posting entries to a node:

XMPPConnection.DEBUG_ENABLED = true;
String userName = “user@domainName”;
String domainName = “domain”;
    ConnectionConfiguration config =
            new ConnectionConfiguration("domainName", 5222);
    config.setReconnectionAllowed(true);
XMPPConnection conn = new XMPPConnection(config);
try {
        conn.connect();
conn.login(userName, “password”);
Date d = new Date();
        d.setTime(System.currentTimeMillis());          
        final String title = "blah";
final String summary = “blahblah”;
final String blogName = “existingBlog”;
final IQ iq = new IQ() {
        @Override
        public String getChildElementXML() {
            return "<pubsub xmlns='http://jabber.org/protocol/pubsub'>" +
                    "<publish node='"+blogName+"'>" +
                    "<item>" +
                    "<entry xmlns='http://www.w3.org/2005/Atom'>" +
                    "<title>"+title+"</title>" +
                    "<summary>"+summary+"</summary>" +
                    "<id>tag:domain,'"+System.currentTimeMillis()+"'</id>" +
                    "<published>"+ d.toString()+"</published>" +
                    "<updated>"+ d.toString()+"</updated>" +
                    "</entry>" +
                    "</item>" +
                    "</publish>" +
                    "</pubsub>";
}
};
    iq.setType(IQ.Type.SET);
    iq.setFrom(userName);
    iq.setTo("pubsub."+domainName);
    conn.sendPacket(iq);
} catch (XMPPException ex) {
        Logger.getLogger(ServiceTest.class.getName()).log(Level.SEVERE, null, ex);
    }

That works just fine.

Here is the code I am using to attempt to retrieve that node entry (and any other possible entries)

XMPPConnection.DEBUG_ENABLED = true;
String userName = “username@domain”;
String domainName = “domain”;
ConnectionConfiguration config =
new ConnectionConfiguration(domainName, 5222);
config.setReconnectionAllowed(true);
XMPPConnection conn = new XMPPConnection(config);
try {
conn.connect();
conn.login(userName, “password”);
// add listener for pubsub iq messages
PacketFilter pf =
new FromContainsFilter(“pubsub.”+domainName);
PacketListener myListener = new PacketListener() {
public void processPacket(Packet arg0) {
System.out.println("xml: " + arg0.toXML());
}
};
conn.addPacketListener(myListener, pf);
final String blogName = “existingBlog”;
final IQ iq = new IQ() {
        @Override
        public String getChildElementXML() {
            return "<pubsub xmlns='http://jabber.org/protocol/pubsub'>"+
                    "<items node='"+blogName+"' /></pubsub>";
        }
    };
    iq.setType(IQ.Type.GET);
    iq.setFrom(from);
    iq.setTo(domain);
    ///iq.setPacketID(topicName)
    conn.sendPacket(iq);
} catch (XMPPException ex) {
Logger.getLogger(ServiceTest.class.getName()).log(Level.SEVERE, null, ex);
}

Watching this process in the Smack Debugger, I can see that the server returns the full message:

title blah

Message #1: Stuff happened!tag:domain,'1282591346850’Mon Aug 23 19:22:26 GMT 2010Mon Aug 23 19:22:26 GMT 2010blah title 2 Message #2: More stuff happened!tag:domain,'1282591409915’Mon Aug 23 19:23:29 GMT 2010Mon Aug 23 19:23:29 GMT 2010

The problem is that in Smack, when receiving the Packet, all I get from the .toXML() is:

What I want is to retrieve the items from the list and their details…

I’ve seen some posts along similiar lines and I have a feeling I have to come to an understanding of the ‘Provider Architecture’, or to use PacketExtensions or some combination of those things…

Can someone point me in the proper direction? Is it possible to simply get the full message from the server and parse it on my own?

edit: I have this figured out and I will post a sample shortly in the off chance it will help anyone in the future.

The feature to enable this is documented (sparsely) here and could certainly use a more detailed description with some examples.

I’m going to attempt to post what I did to get what I want, this is going to be a lengthy post.

Let me know if there is a better way to designate blocks of text as code.

This example assumes you’ve created a pubsub node using a raw packet (or any other method I suppose).

First: Posting a blog entry Here is the way I post a blog entry: (*Note) this node (blogName) was created and configured to know the type of entry it would have, not sure if that matters.

public static void PublishEntryToNode(XMPPConnection conn,

** String from, String domain, final String author,**

** final String summary, final String blogName) {**

** if(conn == null) {**

** return;**

** }**

**
**

** final IQ iq = new IQ() {**

** @Override**

** public String getChildElementXML() {**

** return “” +**

** “” +**

** “” +**

** “” +**

** “”+author+"" +**

** “”+summary+"" +**

** “”+ System.currentTimeMillis()+"" +**

** “” +**

** “” +**

** “” +**

** “”;**

** }**

**
**

** };**

** iq.setType(IQ.Type.SET);**

** iq.setFrom(from);**

** iq.setTo(domain);**

** conn.sendPacket(iq);**

** }**

*Of special note in the above code section is the line:

This namespace is set up using the ‘Provider Architecture’ which is explained in the Smack docs, but I will explain it for this example:

Next: Create a smack.providers xml file. Create a META-INF directory that will be bundled into the root of your jar. If you’re using Netbeans, simply create an empty folder named ‘META-INF’ at the root of the ‘Source Packages’ directory. In that directory create an empty file named ‘smack.providers’.

Here are the contents of my smack.providers file:

<?xml version="1.0"?>


entry
jabber:iq:bot
libxmpp.BotPacket

The elementName and namespace match the line from the first step. We’re telling smack that when you encounter this tag and namespace combo to send the packet as an instance of the class specified in className. libxmpp.BotPacket is the class I created to handle this element & namespace combo.

Next: Writing the BotPacket class. According to Smack’s documentation on Provider architecture, this class must either extend IQ or implement IQProvider. I take the latter route. There are a few other classes I made that go with the BotPacket class which will follow.

public class BotPacket implements IQProvider {

private BotHistory history = null;

**private BotNotification bn = null;
**

public IQ parseIQ(final XmlPullParser arg0) {
try {

** history = new BotHistory();
**

** processDocument(arg0);

    } catch (XmlPullParserException ex) {
        ex.printStackTrace();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
    return history;**

**
**

} // end parseIQ

**
**

**public void processDocument(XmlPullParser xpp)
throws XmlPullParserException, IOException
{
int eventType = xpp.getEventType();
boolean keepGoing = true;
boolean hitStartTag = false;
boolean hitEndTag = false;
boolean hitTextTag = false;

    while(keepGoing) {
        if(eventType == XmlPullParser.START_DOCUMENT) {
            System.out.println("Start document");
        } else if(eventType == XmlPullParser.END_DOCUMENT) {
            System.out.println("End document");
        } else if(eventType == XmlPullParser.START_TAG) {
            startTag = processStartElement(xpp);
            hitStartTag= true;
            hitEndTag = false;
            hitTextTag = false;
        } else if(eventType == XmlPullParser.END_TAG) {
            endTag = processEndElement(xpp);
            hitEndTag = true;
            hitStartTag = false;
            hitTextTag = false;**

__ /* I had an odd error where this loop would hang if it got further than this, if xpp.next() has no next, odd behavior occurs */
if(endTag.equals(“pubsub”)) {
keepGoing = false;
continue;
}
} else if(eventType == XmlPullParser.TEXT) {
text = processText(xpp);
hitEndTag = false;
hitStartTag = false;
hitTextTag = true;
}
processEvent(hitStartTag, hitEndTag, hitTextTag, startTag, endTag, text);

        eventType = xpp.next();
    }

__

** } **

private void processEvent(boolean hitStartTag, boolean hitEndTag, boolean

** hitTextTag,
**

** String startTag, String endTag, String text) {
**

** if(hitStartTag) {
**

** if(startTag.equalsIgnoreCase(“entry”)) {
**

** bn = new BotNotification();
**

** }
**

** } else if(hitEndTag) {
**

** if(endTag.equalsIgnoreCase(“entry”)) {
**

** history.getBotNotifications().add(bn);
**

** }
**

** } else if(hitTextTag) {
**

** if(startTag.equalsIgnoreCase(“author”)) {
**

** bn.setAuthor(text);
**

** } else if(startTag.equalsIgnoreCase(“msg”)) {
**

** bn.setMsg(text);
**

** } else if(startTag.equalsIgnoreCase(“time”)) {
**

** bn.setTime(text);
**

** }
**

** } **


** }**


**
private String processEndElement(XmlPullParser xpp) {
String name = xpp.getName();
String uri = xpp.getNamespace();
if ("".equals (uri))
System.out.println("End element: " + name);
else
System.out.println(“End element: {” + uri + “}” + name);
return name; **


** } **


** private String processStartElement(XmlPullParser xpp) {
String name = xpp.getName();
String uri = xpp.getNamespace();
if ("".equals (uri)) {
System.out.println("Start element: " + name);
} else {
System.out.println(“Start element: {” + uri + “}” + name);
}
return name;
**

** } **


** private String processText(XmlPullParser xpp) {
char ch[] = xpp.getTextCharacters(holderForStartAndLength);
int start = holderForStartAndLength[0];
int length = holderForStartAndLength[1];
System.out.print(“Characters: “”);
StringBuffer sb = new StringBuffer();
for (int i = start; i < start + length; i++) {
switch (ch[i]) {
case ‘\’:
System.out.print(”\\");
sb.append("\\");
break;
case ‘"’:
System.out.print("\"");
sb.append("\"");
break;
case ‘\n’:
System.out.print("\n");
sb.append("\n");
break;
case ‘\r’:
System.out.print("\r");
sb.append("\r");
break;
case ‘\t’:
System.out.print("\t");
sb.append("\t");
break;
default:
System.out.print(ch[i]);
sb.append(ch[i]);
break;
}
}
text = sb.toString();
System.out.print(""\n");
return text;
}**

}

This class implements one method: public IQ parseIQ(final XmlPullParser arg0) {}

I extended IQ with the class BotHistory so I could return an object that was meaningful for me.

**public class BotHistory extends IQ { **


** private ArrayList botNotifications = null; **


** public BotHistory() {
botNotifications = new ArrayList();
**

** } **


** @Override
public String getChildElementXML() {
return “”;
} **


__ /**
* @return the botNotifications
*/
public ArrayList getBotNotifications() {
return botNotifications;
__

** } **


__ /**
* @param botNotifications the botNotifications to set
*/
public void setBotNotifications(ArrayList botNotifications) {
this.botNotifications = botNotifications;
} __


** }**

**
**

and one last helper class:

**public class BotNotification {
private String author;
private String time;
private String msg; **


__ /**
* @return the author
*/
public String getAuthor() {
return author;
__

** } **


__ /**
* @param author the author to set
*/
public void setAuthor(String author) {
this.author = author;
} __


__ /**
* @return the time
*/
public String getTime() {
return time;
__

** } **


__ /**
* @param time the time to set
*/
public void setTime(String time) {
this.time = time;
} __


__ /**
* @return the msg
*/
public String getMsg() {
return msg;
__

** } **


__ /**
* @param msg the msg to set
*/
public void setMsg(String msg) {
this.msg = msg;
} __


** }**

Finally: Catching the packets (a test main)

**public static void main(String[] args) {
XMPPConnection.DEBUG_ENABLED = true;
ConnectionConfiguration config =
new ConnectionConfiguration(“domain”, 5222);
config.setReconnectionAllowed(true);
XMPPConnection conn = new XMPPConnection(config); **


** try {
conn.connect();
conn.login(“user@domain”, “password”); **


** // add listener for pubsub iq messages
PacketFilter pf =
new FromContainsFilter(“pubsub.domain”);
PacketListener myListener = new PacketListener() {
public void processPacket(Packet arg0) {
System.out.println(“in process packet”);
System.out.println("packet class: " + arg0.getClass());
if(arg0 instanceof BotHistory) {
BotHistory bh = (BotHistory)arg0;
for(BotNotification bn : bh.getBotNotifications()) {
System.out.println(bn.getAuthor());
System.out.println(bn.getTime());
System.out.println(bn.getMsg());
}
}

            }
        };
        conn.addPacketListener(myListener, pf); **


**
XMPPUtil.ViewAllTopicEntries(conn, “user@domain”, “pubsub.domain”
, “existingBlogName”); **


** //conn.disconnect();
//System.exit(0);
} catch (XMPPException ex) {
Logger.getLogger(ServiceTest.class.getName()).log(Level.SEVERE, null, ex);
}
**

** }**

the util method:

**public static void ViewAllTopicEntries(XMPPConnection conn,
String from, String domain, final String topicName) { **


** if(conn == null) {
return;
} **


** final IQ iq = new IQ() {
@Override
public String getChildElementXML() {
return “”+
"";
}
};
iq.setType(IQ.Type.GET);
iq.setFrom(from);
iq.setTo(domain);
///iq.setPacketID(topicName)
conn.sendPacket(iq);
**

** }**

If any information is missing to make this post helpful, please let me know.